Theming & Slots
Retheme the widget with a typed prop or plain CSS, switch to dark mode, and replace individual pieces of chrome without forking the component.
The theme prop
The friendliest option for React hosts — pass a typed object and the widget maps it to the equivalent --cf-* CSS variables on its root element.
<ChatterflyWidget
{...props}
theme={{
primary: "#0ea5e9",
radius: "4px",
font: '"Inter", system-ui, sans-serif',
}}
/>CSS variables
Override any of these on an ancestor element. Every Theming v2 token (bubbles, header, input background, shadow, spacing, avatar size) falls back to a primitive above it, so you only need to set what you want to change.
| Variable | theme key | Default |
|---|---|---|
| --cf-primary | primary — #5B4AE4 | |
| --cf-accent | accent — #F28B30 | |
| --cf-info | info — #3A8FD6 | |
| --cf-bg | bg — #ffffff | |
| --cf-fg | fg — #0C1B3A | |
| --cf-muted | muted — #1E3A5E | |
| --cf-border | border — #E8EDF6 | |
| --cf-surface | surface — #F4F6FC | |
| --cf-error | error — #E04848 | |
| --cf-radius | radius — 10px | |
| --cf-font | font — IBM Plex Sans | |
| --cf-height | height — 480px (inline mode) | |
| --cf-bubble-bot-bg / --cf-bubble-bot-fg | bubbleBotBg / bubbleBotFg — var(--cf-surface) / var(--cf-fg) | |
| --cf-bubble-user-bg / --cf-bubble-user-fg | bubbleUserBg / bubbleUserFg — var(--cf-primary) / #ffffff | |
| --cf-header-bg / --cf-header-fg | headerBg / headerFg — reserved for the header slot | |
| --cf-input-bg | inputBg — var(--cf-bg) | |
| --cf-shadow | shadow — none | |
| --cf-spacing | spacing — 1rem | |
| --cf-avatar-size | avatarSize — 28px (reserved) |
.my-widget-host {
--cf-primary: #0ea5e9;
--cf-radius: 4px;
--cf-font: "Inter", system-ui, sans-serif;
}Dark mode
<ChatterflyWidget {...props} colorScheme="dark" /> {/* explicit preset */}
<ChatterflyWidget {...props} colorScheme="auto" /> {/* follows prefers-color-scheme */}colorSchemeships a complete dark token set — you don't need to override every variable yourself to support dark mode.Chrome slots
For layout changes beyond CSS variables, render your own content around the conversation:
<ChatterflyWidget
{...props}
header={<MyBrandHeader />}
footer={<MyPoweredByFooter />}
emptyState={<MySkeleton />}
completedState={<MyThankYouCard />}
errorState={<MyErrorCard />}
/>header/footerrender on the conversational states (main render, replaying, deliberation chat, voice input) — not on the distinct full-panel form/editor layouts, where a chat header isn't a natural fit.
Bubble overrides
<ChatterflyWidget
{...props}
components={{
BotBubble: ({ children }) => <div className="my-bot-bubble">{children}</div>,
UserBubble: ({ children }) => <div className="my-user-bubble">{children}</div>,
}}
/>