> ## Documentation Index
> Fetch the complete documentation index at: https://docs.waniwani.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Theming & customization

> Rebrand the chat widget — colors, typography, message bubbles, and deep CSS customization — on both the script embed and the React component.

Every surface of the chat widget is themeable through the same token system, whether you use the [script embed](/sdk/chat/embed) or the [React component](/sdk/chat/react). Defaults are pinned so an untouched widget looks the same across releases — you only override what you care about.

## Presets

Pick `light` (default), `dark`, or `auto` (follows the visitor's `prefers-color-scheme`):

```html theme={null}
<!-- Script embed -->
<script src="…/embed.js" data-token="wwp_..." data-theme="dark"></script>
```

```tsx theme={null}
// React
<WaniwaniChat token="wwp_..." channelId="..." overrides={{ appearance: { theme: "auto" } }} />
```

## Theme tokens

Layer per-property overrides on top of a preset. The same keys work everywhere:

```tsx theme={null}
// React — ChatEmbed or WaniwaniChat (via overrides.appearance)
<ChatEmbed
  api="/api/my-chat"
  appearance={{
    theme: "light",
    variables: {
      primaryColor: "#0a6c74",
      userBubbleColor: "#0a6c74",
      userBubbleTextColor: "#ffffff",
      messageBorderRadius: 20,
      messagePaddingX: 18,
      messageMaxWidth: "75%",
      fontFamily: "Georgia, serif",
      fontSize: 15,
      lineHeight: "1.6",
    },
  }}
/>
```

```js theme={null}
// Script embed — programmatic init
window.WaniWani.chat.init({
  token: "wwp_...",
  appearance: {
    theme: "light",
    variables: { primaryColor: "#0a6c74", messageBorderRadius: 20 },
  },
});
```

## CSS variables

The widget exposes a `--ww-*` namespace that pierces the Shadow DOM boundary, so the script embed can be themed from your page's own CSS:

```html theme={null}
<style>
  [data-waniwani-embed] {
    --ww-primary: #0a6c74;
    --ww-msg-radius: 20px;
    --ww-font: "Inter", sans-serif;
  }
</style>
<div data-waniwani-embed></div>
```

Unset variables fall back to the preset's defaults, and your overrides win in both light and dark modes.

| CSS Variable                 | Theme token                | Property                 | Default (light) | Default (dark) |
| ---------------------------- | -------------------------- | ------------------------ | --------------- | -------------- |
| `--ww-primary`               | `primaryColor`             | Primary brand colour     | `#6366f1`       | `#6366f1`      |
| `--ww-primary-fg`            | `primaryForeground`        | Text on primary          | `#1f2937`       | `#ffffff`      |
| `--ww-bg`                    | `backgroundColor`          | Panel background         | `#ffffff`       | `#212121`      |
| `--ww-text`                  | `textColor`                | Default text colour      | `#1f2937`       | `#ececec`      |
| `--ww-muted`                 | `mutedColor`               | Secondary text           | `#6b7280`       | `#8e8ea0`      |
| `--ww-border`                | `borderColor`              | Border colour            | `#e5e7eb`       | `#444444`      |
| `--ww-assistant-bubble`      | `assistantBubbleColor`     | Assistant bubble bg      | `#f3f4f6`       | `#2f2f2f`      |
| `--ww-assistant-bubble-text` | `assistantBubbleTextColor` | Assistant bubble text    | `#1f2937`       | `#ececec`      |
| `--ww-user-bubble`           | `userBubbleColor`          | User bubble bg           | `#f4f4f4`       | `#303030`      |
| `--ww-user-bubble-text`      | `userBubbleTextColor`      | User bubble text         | `#1f2937`       | `#ffffff`      |
| `--ww-input-bg`              | `inputBackgroundColor`     | Input field bg           | `#f9fafb`       | `#2f2f2f`      |
| `--ww-header-bg`             | `headerBackgroundColor`    | Header background        | `#ffffff`       | `#1e1e1e`      |
| `--ww-header-text`           | `headerTextColor`          | Header text              | `#1f2937`       | `#ececec`      |
| `--ww-status`                | `statusColor`              | Status dot               | `#22c55e`       | `#22c55e`      |
| `--ww-tool-card`             | `toolCardColor`            | Tool call card bg        | `#f4f4f5`       | `#262626`      |
| `--ww-radius`                | `borderRadius`             | Panel border-radius      | `16px`          | `16px`         |
| `--ww-msg-radius`            | `messageBorderRadius`      | Message bubble radius    | `8px`           | `8px`          |
| `--ww-msg-pad-x`             | `messagePaddingX`          | Message bubble padding X | `16px`          | `16px`         |
| `--ww-msg-pad-y`             | `messagePaddingY`          | Message bubble padding Y | `12px`          | `12px`         |
| `--ww-msg-max-width`         | `messageMaxWidth`          | Message bubble max width | `80%`           | `80%`          |
| `--ww-font`                  | `fontFamily`               | Font family              | system stack    | system stack   |
| `--ww-font-size`             | `fontSize`                 | Base message font size   | `1rem`          | `1rem`         |
| `--ww-line-height`           | `lineHeight`               | Base message line height | `1.5`           | `1.5`          |

## Assistant message bubble (opt-in)

Assistant replies render as plain text by default. Turn them into filled bubbles — styled by `assistantBubbleColor` / `assistantBubbleTextColor`, sharing the user bubble's radius and padding — with `assistantBubble`:

```html theme={null}
<!-- Script tag: "true"/"1" or a bare attribute enable it; "false" forces it off -->
<script src="…/embed.js" data-token="wwp_..." data-assistant-bubble="true"></script>
```

```tsx theme={null}
// React
<WaniwaniChat
  token="wwp_..."
  channelId="..."
  overrides={{ appearance: { assistantBubble: true } }}
/>
```

Leave it unset and assistant messages stay bubble-less — existing widgets are unchanged.

## Deep customization

Tokens cover colors, typography, and bubble shape. To restyle an element beyond what a token exposes, target it directly.

**React** — pass `classNames` (available on `ChatEmbed` directly, or `overrides.classNames` on `WaniwaniChat`). Each string is merged onto that slot:

```tsx theme={null}
<WaniwaniChat
  token="wwp_..."
  channelId="..."
  overrides={{
    classNames: {
      root: "my-chat",
      header: "ww:uppercase ww:tracking-wide",
      userBubble: "ww:shadow-md",
      input: "ww:ring-2 ww:ring-[#0a6c74]",
    },
  }}
/>
```

Slots: `root`, `header`, `message`, `userBubble`, `assistantBubble`, `input`.

**Script embed** — the widget renders in a Shadow DOM, so your page's own selectors cannot reach inside. Inject a stylesheet with `data-css` and target the stable semantic classes:

```html theme={null}
<script
  src="…/embed.js"
  data-token="wwp_..."
  data-css="https://your.site/widget.css"
></script>
```

```css theme={null}
/* widget.css — loaded into the widget's Shadow DOM */
.ww-header { letter-spacing: 0.02em; }
.ww-input { box-shadow: 0 0 0 2px rgba(10, 108, 116, 0.25); }
.ww-message-user .ww-bubble { box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); }
.ww-message-assistant .ww-bubble { border: 1px solid #0a6c74; }
```

Stable hooks: `.ww-message`, `.ww-message-user`, `.ww-message-assistant`, `.ww-bubble`, `.ww-header`, `.ww-input`. These are guaranteed selectors — the internal `ww:`-prefixed utility classes are not.

## What is not themeable

To set expectations for a full rebrand, these are intentionally fixed today:

* **Typographic scale beyond the base** — `fontSize` / `lineHeight` set the message base; per-heading sizes inside markdown and built-in label sizes/weights are not individually exposed.
* **Chrome corner radii** — `messageBorderRadius` shapes message bubbles only; tool-call cards, the chain-of-thought accordion, menus, and welcome-screen buttons keep their fixed radii.
* **Floating launcher accent** — the dock's animated border-glow uses a fixed accent gradient.
* **"Powered by" mark** — the footer logo is not removable via theming.

For anything in this list, [reach out](https://waniwani.ai) — some are candidates for future tokens.

Full type definitions: [`ChatTheme` and `ChatClassNames`](https://github.com/WaniWani-AI/sdk/blob/main/src/chat/web/%40types.ts).
