Skip to main content

Theming

Three layers, in priority order:

  1. CSS variables — instant override; works inside Shadow DOM.
  2. ThemeConfig — JSON object passed to the store; renders to a CSS variable map at runtime.
  3. Defaults — DEFAULT_THEME (in domain/value-objects/Theme.ts).

CSS variables​

Set on :root or any ancestor of <chat-iva>. They pierce the Shadow DOM:

chat-iva {
--chativa-primary-color: #4f46e5;
--chativa-secondary-color: #6c757d;
--chativa-background-color: #ffffff;
--chativa-text-color: #212529;
--chativa-border-color: #dee2e6;
}

The store's setTheme() ultimately writes these same variables via themeToCSS().

ThemeConfig​

Full schema: schemas/theme.schema.json. Field-by-field reference: configuration → ThemeConfig.

import { chatStore } from "@chativa/core";

chatStore.getState().setTheme({
colors: { primary: "#0ea5e9", secondary: "#0284c7" },
position: "bottom-left",
size: "large",
layout: { width: "400px", height: "600px" },
});

Buttons in each buttons message share the width of that list's longest label. The width adjusts automatically as labels change, without measuring text in JavaScript. On narrow screens the list fits the available space and labels wrap. This replaces the previous fixed 220px default.

To set a fixed width:

chatStore.getState().setTheme({ buttonWidth: "280px" });

Use buttonWidth: "auto" to restore automatic sizing. Fixed widths also remain limited to the available space. With the theme option unset, you can instead set --chativa-button-width: 280px on an ancestor of <chat-iva> (use max-content for automatic sizing through CSS).

Set buttonTextAlign to "left", "center", or "right" for buttons messages. The default is "center"; wrapped label lines use the same alignment. Card actions and quick replies keep their existing styling.

chatStore.getState().setTheme({ buttonTextAlign: "left" });

Alternatively, when the theme leaves this option unset, use the inherited CSS variable:

chat-iva {
--chativa-button-text-align: left;
}

Position and RTL​

position names a physical corner of the viewport. "bottom-right" means the bottom-right corner in every language, including Arabic or Hebrew. The launcher, the popup next to it, and the edge a side-panel docks to and slides in from all use that physical corner. Content inside the widget does mirror under RTL (see Internationalisation → Right-to-left languages).

The default position is "bottom-right". A page's own layout decides where a floating button looks right, and Chativa can't tell the default value apart from one you set explicitly. If your site is RTL and you want the launcher on the inline-end side, pick the corner yourself:

import { i18next } from "@chativa/ui";
import { chatStore } from "@chativa/core";

const rtl = i18next.dir(i18next.language) === "rtl";
chatStore.getState().setTheme({ position: rtl ? "bottom-left" : "bottom-right" });

(isRtlLanguage() from @chativa/ui gives the same answer for the four languages Chativa mirrors.)

Window modes​

ModeBehaviour
popup (default)Floating card anchored to the launcher.
side-panelFull-height drawer docked to the viewport edge.
fullscreenCovers the whole viewport. Always used on mobile (<480 px).
inlinePositioned statically inside its parent — for embedding in a page section.

Popup (default — floating card)

Popup mode

Side panel (full-height drawer)

Side panel mode

Fullscreen (covers the viewport)

Fullscreen mode

Captured from the live sandbox. The inline mode looks the same as popup when no parent layout constrains it — drop the widget inside any container to see the difference.

Locking the widget to fullscreen​

fullscreen-only starts the widget expanded and hides the header's fullscreen toggle — the shorthand for setFullscreen(true) + setAllowFullscreen(false):

<chat-iva fullscreen-only></chat-iva>
import { ChatIva } from "@chativa/react";

<ChatIva fullscreenOnly />

Passing false (or omitting it) means "no opinion": the widget keeps whatever windowMode and allowFullscreen the theme already set, rather than forcing the toggle back on.

Avatars​

chatStore.getState().setTheme({
avatar: {
bot: "https://example.com/bot.png",
header: "https://example.com/header.png",
user: "https://example.com/user.png",
showBot: true,
showUser: false,
},
});

Omit any URL to fall back to the default SVG.

avatar.bot and showBot apply to every bot-authored bubble — including the buttons and quick-reply message types, which render the configured image (or the default robot SVG when no URL is set) next to their button list. showBot: false hides the avatar there too.

Custom launcher button​

The launcher accepts both a slot and a ::part(launcher) selector:

<style>
chat-bot-button::part(launcher) {
border-radius: 24px;
width: 140px;
height: 48px;
background: linear-gradient(135deg, #e91e63, #ff5722);
}
</style>

<chat-bot-button>
<span style="color:white;font-weight:600;">💬 Bize Yazın</span>
</chat-bot-button>

When a custom launcher is used, set hideButtonOnOpen: true to hide it while the chat is open.

Fluent builder​

import { ThemeBuilder } from "@chativa/core";

const theme = ThemeBuilder.create()
.setPrimary("#0ea5e9")
.setBackground("#1e1e2e")
.setText("#f8fafc")
.setPosition("bottom-left")
.setSize("large")
.setShowMessageStatus(true)
.setEnableSearch(false)
.build();

chatStore.getState().setTheme(theme);

The Chrome extension (chrome-extension) generates this exact builder code from a visual editor — handy for "design now, paste into your app later" workflows.

Live preview​

The sandbox lets you tweak every field and see the result instantly: chativa.aimtune.dev/sandbox.