Theming
The chat interface resolves its colours from three layers. Understanding the order is the whole of this page.
Precedencelink
Remote / dashboard theme highest priority
overrides
Local / SDK theme
overrides
Native / built-in theme final fallbackThe merchant's dashboard configuration wins, because the merchant owns how their support experience looks. Your local theme fills in whatever the dashboard did not set. The built-in theme fills in whatever neither of those set, so every colour always has a value.
Merging is per property, not per objectlink
Layers are merged token by token, not swapped wholesale. Setting one colour locally does not discard the rest of the theme.
Built-in: headerBackground = purple, chatBackground = off-white, sentBubble = purple
Local: headerBackground = green
Remote: sentBubble = navy
Resolved: headerBackground = green (remote silent, local wins)
sentBubble = navy (remote wins)
chatBackground = off-white (neither set it, built-in wins)Nested structures (the background image block) merge the same way: a remote opacity does not erase a local image URL.
The colour tokenslink
Every token names a visible part of the interface, and the same names are used by the dashboard, the web widget and all four mobile SDKs.
chatBackground, headerBackground, headerPrimaryText, headerSecondaryText, headerIcon, closeButton, receivedBubble, receivedText, receivedTimestamp, sentBubble, sentText, sentTimestamp, systemMessageText, daySeparatorBackground, daySeparatorText, footerContainer, chatInputBackground, chatInputText, chatInputPlaceholder, chatInputBorder, sendButtonBackground, sendButtonIcon, attachmentButton, emojiButton, typingIndicatorBackground, typingIndicatorDot, unreadBadgeBackground, unreadBadgeText, scrollToBottomButtonBackground, scrollToBottomButtonIcon, onlineStatus, offlineStatus, error, success, warning.
Values are CSS colours on the web and hex strings (#RRGGBB or #AARRGGBB) on mobile. An unusable value is ignored and the next layer down applies - a bad colour never breaks the interface.
Light and darklink
mode picks which built-in palette forms the base. It follows the same precedence, with one rule: the dashboard's auto means "no opinion" and does not consume its turn, so your local dark still applies when the merchant has not pinned a mode. If nobody pins one, the viewer's system preference decides.
Remote mode = light | dark -> wins
Remote mode = auto (or none) -> your local mode wins
Neither -> the device / browser settingSetting a local themelink
<!-- Web: script tag. A preset, or JSON for individual tokens. -->
<script src="https://widget.newinstance.cloud/embed.js"
data-api-key="sk_live_abc123"
data-theme='{"mode":"dark","colors":{"sentBubble":"#16a34a"}}'
></script>NInstanceChat.init({
apiKey: 'sk_live_abc123',
theme: { mode: 'dark', colors: { sentBubble: '#16a34a' } },
});LiveAndAiChatConfig(
apiKey = "sk_live_abc123",
theme = ChatThemeOverride(sentBubble = "#16A34A", mode = ChatThemeOverride.Mode.DARK),
)try LiveAndAiChatConfig(
apiKey: "sk_live_abc123",
theme: ChatThemeOverride(mode: .dark, sentBubble: "#16A34A")
)new NewinstanceChat({
apiKey: 'sk_live_abc123',
theme: { mode: 'dark', sentBubble: '#16A34A' },
});NewinstanceChatConfig(
apiKey: 'sk_live_abc123',
theme: ChatThemeOverride(mode: 'dark', sentBubble: '#16A34A'),
);The web widget also accepts 'dark' / 'light' as a bare string, and still accepts the older flat palette (primaryColor, backgroundColor, textColor, and so on), which is translated onto the tokens above.
When configuration cannot be fetchedlink
An invalid key, a disabled chat, an unreachable backend, a timeout, a malformed response - none of these leave the interface unstyled.
Remote configuration loads -> remote + local + built-in
Remote configuration fails -> local + built-in
No local theme either -> built-inA previously valid local theme is never destroyed by a failed fetch, the interface never sits blank or half-painted, and no authentication or configuration detail is shown to the customer. The failure is reported to you on the error channel as CONFIG_FETCH_FAILED (see Authentication and identity -> Diagnosing problems), which is a warning, not a fatal error.
A partly usable remote response is used partly: the tokens that parsed are applied, and the ones that did not fall through to your local theme and then the built-in palette.
Authentication and theming are independentlink
They fail independently and are handled independently.
| Authentication | Remote theme | Result |
|---|---|---|
| Succeeds | Loads | Chat works, fully themed by the dashboard |
| Succeeds | Fails | Chat works, themed by your local theme and the built-in palette |
| Anonymous visitor | Loads | Chat works, fully themed - identity is irrelevant to theming |
| Fails | n/a | Chat does not open, and you get a coded diagnostic |
One resolution, one interfacelink
Theme precedence is resolved in exactly one place per SDK, and every part of the interface - header, message list, composer, day separators, buttons, badges - consumes that one resolved theme. There is no path by which the header can render the dashboard theme while the composer renders yours.