Skip to main content

Theming

All visual styles are driven by CSS custom properties. Override them on :root, <i-chat>, or any ancestor to customize the look and feel — no need to touch the source.

Host theme contract (light / dark)

Built-in pieces that must track page light/dark — fenced chart blocks (ECharts / @bndynet/icharts) and fenced mermaid blocks — all follow the same rules on the document root (document.documentElement, i.e. <html>):

SignalDark mode
class<html class="…"> includes the token dark (e.g. Tailwind / Element Plus style class="dark").
data-themeThe attribute value contains the substring dark (e.g. dark, github-dark, preferred-color-scheme-dark).

If neither applies, the page is treated as light for these integrations.

Set the contract on <html> so chart and Mermaid stay aligned with your app chrome. Example (JS):

document.documentElement.setAttribute('data-theme', 'dark');
document.documentElement.classList.add('dark');

Toggling only <body> or a nested wrapper without updating <html> may leave charts/Mermaid on the previous palette.

What the library watches: a MutationObserver on <html> for data-theme and class. Charts call @bndynet/icharts switchTheme('dark' | 'light'). Mermaid re-runs render() with theme: 'base', darkMode derived from the same signals, and themeVariables merged from optional --chat-mermaid-* tokens (falling back to --chat-* as in Mermaid CSS custom properties). Message bodies use Shadow DOM; the implementation walks open shadow roots to find <i-chart> / <i-chat-mermaid> so diagrams inside bubbles still update.

CSS-only dark: the Quick example — dark theme below uses :root[data-theme="dark"] { … }. You can instead put the dark class on <html> and drive --chat-* from html.dark { … } — both satisfy the contract above.

Limitations: Closed shadow trees or theme flags set only on inner nodes (never reflected on <html>) are invisible to this contract; use <html> for global theme, or supply your own BlockRenderer / styling.

Mermaid CSS custom properties

Fenced mermaid blocks render inside <i-chat-mermaid>. The renderer uses Mermaid theme: 'base' and fills themeVariables from getComputedStyle(host) on that element: for each --chat-mermaid-* below, if the value is empty after trim(), the listed --chat-* fallbacks are read in order (still on the same host, so inherited :root tokens apply). You do not need to define --chat-mermaid-* unless you want diagram-only overrides.

PropertyDerives from (read order)Description
--chat-mermaid-background--chat-bgDiagram canvas / outer background
--chat-mermaid-text--chat-textPrimary labels and node text
--chat-mermaid-text-secondary--chat-text-secondarySecondary labels (loops, tertiary text)
--chat-mermaid-line--chat-borderLines, borders, arrows, links
--chat-mermaid-node-fill--chat-surface-altPrimary block fill (first in the shared node-fillmain-fill--chat-surface-alt chain)
--chat-mermaid-cluster-fill--chat-surface-altCluster / subgraph fill
--chat-mermaid-main-fill--chat-surfaceSecond choice in the same block-fill chain if node-fill is unset (not a separate layer for flowchart nodes)
--chat-mermaid-tertiary-fill--chat-surfaceTertiary fills
--chat-mermaid-primary--chat-primaryAccent (primary-colored elements)
--chat-mermaid-primary-contrast--chat-self-text, --chat-user-text, then --chat-textText on primary-colored shapes
--chat-mermaid-secondary-fill--chat-primary-lightSecondary fills (e.g. activation bars)
--chat-mermaid-note-fill--chat-code-bgNote / callout background
--chat-mermaid-note-text--chat-code-textNote / callout text
--chat-mermaid-edge-label-bg--chat-surfaceEdge label background

Why mainBkg / nodeBkg / actors look the same: Mermaid’s flowchart stylesheet uses themeVariables.mainBkg for .node rect fills, while sequence diagrams use actorBkg. The integration resolves one block color from node-fillmain-fill--chat-surface-alt, assigns it to both mainBkg and nodeBkg, and sets actorBkg to that value so flowchart and sequence participant boxes stay aligned.

The TypeScript package also exports CHAT_MERMAID_TOKEN_NAMES from @bndynet/ichat-renderers for tooling or docs.

Token architecture

The library uses a 12 base token system. Every component-specific token (user bubbles, reasoning, timeline, form, input, etc.) automatically derives from these base tokens via var() chaining. Most apps only need to set these 12 properties to completely re-theme the UI:

TokenLight defaultDescription
--chat-bg#f7f7f8 #f7f7f8Container background
--chat-surface#ffffff #ffffffCard / bubble surface
--chat-surface-alt#f0f2f5 #f0f2f5Alternate surface (table headers, summaries)
--chat-border#e5e7eb #e5e7ebBorders and dividers
--chat-text#1a1a2e #1a1a2ePrimary text
--chat-text-secondary#6b7280 #6b7280Secondary text (labels)
--chat-text-muted#9ca3af #9ca3afMuted text (timestamps, placeholders)
--chat-primary#2563eb #2563ebAccent / brand color
--chat-primary-light#dbeafe #dbeafeLight tint of primary
--chat-error#ef4444 #ef4444Error / danger color
--chat-success#10b981 #10b981Success color
--chat-warning#f59e0b #f59e0bWarning color

How derivation works

Component-specific tokens chain to base tokens. You can override any component token for fine-grained control, but you don't have to:

Component tokenDerives from
--chat-user-bg--chat-primary
--chat-assistant-bg--chat-surface
--chat-assistant-text--chat-text
--chat-peer-bg--chat-surface
--chat-reasoning-bg--chat-primary-light
--chat-reasoning-text--chat-primary
--chat-reasoning-bordercolor-mix(--chat-primary, --chat-border)
--chat-error-color--chat-error
--chat-error-bgcolor-mix(--chat-error, --chat-surface)
--chat-timeline-done--chat-success
--chat-timeline-error--chat-error
--chat-kpi-trend-up--chat-success
--chat-kpi-trend-down--chat-error
--chat-input-bg--chat-surface
--chat-input-border--chat-border
--chat-input-text--chat-text
--chat-input-primary--chat-primary
--chat-mermaid-background--chat-bg
--chat-mermaid-text--chat-text
--chat-mermaid-text-secondary--chat-text-secondary
--chat-mermaid-line--chat-border
--chat-mermaid-node-fill--chat-surface-alt
--chat-mermaid-cluster-fill--chat-surface-alt
--chat-mermaid-main-fill--chat-surface, then --chat-surface-alt (after node-fill in the shared block-fill chain)
--chat-mermaid-tertiary-fill--chat-surface
--chat-mermaid-primary--chat-primary
--chat-mermaid-primary-contrast--chat-self-text, --chat-user-text, --chat-text
--chat-mermaid-secondary-fill--chat-primary-light
--chat-mermaid-note-fill--chat-code-bg
--chat-mermaid-note-text--chat-code-text
--chat-mermaid-edge-label-bg--chat-surface
--chat-form-*Various --chat-* base tokens

Quick example — dark theme

With the 12-token architecture, a dark theme only needs the base tokens plus any design-constant overrides (like code block colors). Pair this selector with data-theme on <html> (or use html.dark tokens) so it matches the host theme contract for charts and Mermaid.

:root[data-theme="dark"] {
--chat-bg: #16213e;
--chat-surface: #1e1e3a;
--chat-surface-alt: #2d2d44;
--chat-border: #404060;
--chat-text: #e0e0e0;
--chat-text-secondary:#a0a0b0;
--chat-text-muted: #707080;
--chat-primary: #818cf8;
--chat-primary-light: #312e81;
--chat-error: #f87171;
--chat-success: #10b981;
--chat-warning: #fbbf24;

/* Design constants (not derived from base) */
--chat-code-bg: #0d1117;
--chat-code-text: #c9d1d9;
--chat-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
--chat-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4);
}

CSS custom properties reference

Naming convention: --chat-<category>-<detail>. All properties have sensible light-theme defaults; you only need to override what you want to change.

For avatar colors, each role uses two CSS levels only: --chat-avatar-<role>-{bg|text} then shared --chat-avatar-{bg|text}, then built-in defaults. Set --chat-avatar-bg once to tint every role, or e.g. --chat-avatar-user-bg for role: self only.

Typography

PropertyDefaultDescription
--chat-font-familysystem stackPrimary font family
--chat-font-monoSF Mono, Consolas, …Monospace font for code
--chat-font-size0.9375remBase font size
--chat-font-size-sm0.8125remSmall text (timestamps, labels, code blocks)
--chat-font-size-lg1remLarge text (empty state)
--chat-line-height1.6Base line height for message text

Colors — 12 Base Tokens

PropertyDefaultDescription
--chat-bg#f7f7f8Container background
--chat-surface#ffffffElevated surface (bubbles, scroll button, cards)
--chat-surface-alt#f0f2f5Alternative surface (table headers, charts, action hover)
--chat-border#e5e7ebBorders, dividers, scrollbar thumb
--chat-text#1a1a2ePrimary text color
--chat-text-secondary#6b7280Secondary text (labels, blockquote)
--chat-text-muted#9ca3afMuted text (timestamps, placeholders)
--chat-primary#2563ebAccent / link color, typing cursor
--chat-primary-light#dbeafeLight tint of primary (reasoning bg, highlights)
--chat-error#ef4444Error / danger color
--chat-success#10b981Success color (timeline done, KPI trend up)
--chat-warning#f59e0bWarning color

Colors — Self bubble (role: self)

PropertyDefaultDescription
--chat-self-bg= --chat-user-bgSelf message background
--chat-self-text= --chat-user-textSelf message text
--chat-user-bg= --chat-primaryUser bubble background (derives from primary)
--chat-user-text#ffffffUser bubble text (inverted)
--chat-avatar-user-bgchainSelf avatar ring (--chat-avatar-bg then default)
--chat-self-code-inline-bgchainInline code inside self bubble

Colors — Peer bubble (role: peer)

PropertyDefaultDescription
--chat-peer-bg= --chat-surfacePeer message background (via --chat-assistant-bg)
--chat-peer-text= --chat-textPeer message text (via --chat-assistant-text)
--chat-avatar-peer-bgchainPeer avatar ring (--chat-avatar-bg then default)
--chat-peer-code-inline-bg= --chat-code-inline-bgInline code inside peer bubble

Colors — Assistant bubble

PropertyDefaultDescription
--chat-assistant-bg= --chat-surfaceAssistant message background
--chat-assistant-text= --chat-textAssistant message text
--chat-avatar-assistant-bgchainAssistant avatar (--chat-avatar-bg then default)

Colors — Avatars (system + shared)

PropertyDefaultDescription
--chat-avatar-system-bgchainSystem message avatar (--chat-avatar-bg then default)
--chat-avatar-system-textchainSystem avatar glyph color
--chat-avatar-bg= --chat-surface-altShared avatar background when role token is unset
--chat-avatar-text= --chat-text-secondaryShared avatar glyph color when role token is unset

Colors — Reasoning

PropertyDefaultDescription
--chat-reasoning-bg= --chat-primary-lightReasoning block background
--chat-reasoning-borderderivedReasoning block border (mix of --chat-primary and --chat-border)
--chat-reasoning-text= --chat-primaryReasoning header text
--chat-reasoning-header-hover-bgderivedReasoning header hover overlay

Colors — Code (design constant)

PropertyDefaultDescription
--chat-code-bg#1e1e2eCode block background
--chat-code-text#cdd6f4Code block text
--chat-code-inline-bgrgba(0,0,0,0.06)Inline code background
--chat-user-code-inline-bgrgba(255,255,255,0.15)Inline code inside self bubble

Colors — Status (derived from base)

PropertyDefaultDescription
--chat-error-color= --chat-errorError text color
--chat-error-bgderivedError background (mix of --chat-error and --chat-surface)
--chat-timeline-done= --chat-successTimeline done step indicator
--chat-timeline-active= --chat-primaryTimeline active step indicator
--chat-timeline-error= --chat-errorTimeline error step indicator
--chat-kpi-trend-up= --chat-successKPI positive trend color
--chat-kpi-trend-down= --chat-errorKPI negative trend color

Colors — Misc

PropertyDefaultDescription
--chat-blockquote-bgrgba(0,0,0,0.02)Blockquote background
--chat-chart-bar-track-bgrgba(0,0,0,0.04)Chart bar track background

Spacing

PropertyDefaultDescription
--chat-spacing-xs4pxExtra-small gap
--chat-spacing-sm8pxSmall gap
--chat-spacing-md16pxMedium gap (default padding)
--chat-spacing-lg24pxLarge gap (message list padding)
--chat-spacing-xl32pxExtra-large gap

Border radius

PropertyDefaultDescription
--chat-radius-sm6pxSmall radius (bubble tail, code, images)
--chat-radius12pxMedium radius (container, code blocks, reasoning)
--chat-radius-lg18pxLarge radius (message bubbles)

Shadows

PropertyDefaultDescription
--chat-shadow-sm0 1px 2px rgba(0,0,0,0.05)Assistant bubble shadow
--chat-shadow-md0 4px 12px rgba(0,0,0,0.08)Scroll-to-bottom button shadow

Transitions

PropertyDefaultDescription
--chat-transition-fast150ms easeFast hover/press transitions
--chat-transition-normal250ms easeNormal transitions (message appear, reasoning toggle)

Layout

PropertyDefaultDescription
--chat-avatar-size32pxAvatar width & height
--chat-message-max-width85%Max width of a single message row
--chat-messages-max-width100%Max width of the message list inner area (fills host; override e.g. 800px or min(100%, 48rem) for a centered reading column)
--chat-scrollbar-width6pxScrollbar width (WebKit)

Minimal override set

For most themes you only need to set these 12 base tokens — everything else derives from them automatically:

:root {
--chat-bg:;
--chat-surface:;
--chat-surface-alt:;
--chat-border:;
--chat-text:;
--chat-text-secondary:;
--chat-text-muted:;
--chat-primary:;
--chat-primary-light:;
--chat-error:;
--chat-success:;
--chat-warning:;
}

For fine-grained control, override any component-specific token (e.g. --chat-user-bg, --chat-reasoning-text, --chat-code-bg) — these accept the same var() chaining pattern and fall back to the relevant base token.