# Theming, locale & z-index

> Match the widget to your brand with theme colors, fonts, locale, and z-index control.

Product: Clad Widget SDK
Source: https://docs.useclad.ai/widget/theming

---

Theming is **code‑owned**: you brand the widget entirely from your install code,
not the dashboard. Out of the box the widget renders a **monochrome default** — a
matte near‑black accent on a matte off‑white surface that automatically inverts
to off‑white‑on‑near‑black for dark host pages (when `mode: "system"`). Every
surface is driven by a `--sc-*` design token, so a few values rebrand the
launcher, header, buttons, message bubbles, inputs, and links consistently.

Set the theme initially in `window.SupportChatSettings` (or
`SupportChat.createChat({ theme })`) for the first paint, and/or at runtime via
`chat.setTheme(...)`. Omit `colors` entirely to keep the monochrome default.

```ts
chat.setTheme({
  mode: "system",                // "light" | "dark" | "system" (default: system)
  logoUrl: "https://acme.com/mark.svg", // shown in the widget header (omit → no logo, title only)
  colors: {
    // Every key is optional — anything you omit falls back to the monochrome
    // default token. Example brand palette:
    primary: "#4f46e5",          // accent: buttons, links, focus, launcher, user bubble
    primaryText: "#ffffff",      // text/icon rendered on top of `primary`
    background: "#ffffff",       // panel + header base
    surface: "#f6f7f9",          // conversation/content background
    text: "#1b1d24",
    mutedText: "#6a7282",
    border: "#e8eaf0",
    headerBackground: "#ffffff", // defaults to `background`
    headerText: "#1b1d24",       // defaults to `text`
    launcher: "#4f46e5",         // defaults to `primary`
    launcherIcon: "#ffffff",     // defaults to `primaryText`
    userMessage: "#4f46e5",
    userMessageText: "#ffffff",  // defaults to `primaryText`
    agentMessage: "#ffffff",
    agentMessageText: "#1b1d24", // defaults to `text`
    danger: "#e5484d",
  },
  borderRadius: "lg",            // none | sm | md | lg | xl | <css length> — panel + launcher
  bubbleRadius: "lg",            // none | sm | md | lg | xl | full | <css length> — message bubbles
  elevation: "lg",               // none | sm | md | lg | xl — panel shadow depth
  fontFamily: "Inter, system-ui, sans-serif",
});

chat.setLocale("en-US");
chat.setZIndex(999999);
```

`setTheme` merges with the current theme (including a deep merge of `colors`), so
you can update tokens incrementally (e.g. flip `mode` or one color) without
re‑specifying the whole object.

> A single `primary` gives you a coherent monochrome‑style look in your brand
> color (it drives the launcher, buttons, links, and outbound bubbles). Set the
> neutral tokens (`background`, `surface`, `text`, `border`) too for a fully
> custom palette.

<!--
<div class="img-ph">
  <span class="img-ph__badge">📷 Screenshot needed</span>
  <p class="img-ph__title">A themed widget</p>
  <p class="img-ph__desc">The widget styled to match a brand — custom primary color, font, and rounded corners — ideally next to the default monochrome look, to show what theming achieves.</p>
</div>
-->

### Custom CSS

For pixel‑level control beyond the tokens, inject CSS into the (isolated) widget
iframe via `theme.customCss`. Target the stable `.sc-*` class contract or
override any `--sc-*` custom property. The CSS is applied as a stylesheet
**inside** the sandboxed UI — it can restyle any element but cannot execute
scripts.

```ts
chat.setTheme({
  customCss: `
    .sc-header { background: linear-gradient(90deg, #4f46e5, #7c3aed); --sc-header-text: #fff; }
    .sc-launcher { box-shadow: 0 10px 30px rgba(79,70,229,.45); }
    .sc-msg.in { border-radius: 14px; }
  `,
});
```

> Note: the **"Powered by Clad"** footer is a permanent part of the widget and is
> not removable via theme or custom CSS.

### Header & branding

The header bar is the most‑branded surface. Its anatomy on the home screen is:

```
┌───────────────────────────────────────────────┐
│  [logo?]  Title                            ✕   │   ← .sc-header
└───────────────────────────────────────────────┘
```

By default the title reads **"Support"** and no logo is shown. You can change all
of this:

**1 — Replace the "Support" text.** Set it from your install code with
`content.headerTitle`. The title is, in priority order, `content.headerTitle` →
the legacy `launcher.label` → `"Support"`:

```js
SupportChat.createChat({ workspaceId: "{YOUR_WORKSPACE_ID}", widgetId: "{YOUR_WIDGET_ID}", content: { headerTitle: "Acme Support" } });
```

This copy is configured in code — not the dashboard — so there's a single source
of truth and no risk of conflicting edits. (The widget's **Name** in the
dashboard is just an internal label; visitors never see it.)

**2 — Add a logo next to the title.** Set `theme.logoUrl` to an image URL; it
renders as a small rounded mark to the left of the title (omit it for a clean
title‑only header):

```js
SupportChat.createChat({
  workspaceId: "{YOUR_WORKSPACE_ID}",
  widgetId: "{YOUR_WIDGET_ID}",
  theme: { logoUrl: "https://acme.com/mark.svg" },
  launcher: { label: "Acme Support" },
});
```

**3 — Recolor the header.** Two tokens control it (both default to the panel
`background` / `text`):

```ts
chat.setTheme({ colors: { headerBackground: "#0f172a", headerText: "#ffffff" } });
```

**4 — Go further: banners, gradients, custom type, a bigger logo.** Anything
beyond a solid color is done with [`customCss`](#custom-css) targeting the
header's stable classes. Because `customCss` is a *stylesheet* (it styles
existing elements; it can't inject new DOM), use real elements (the logo image,
the title) plus backgrounds and `::before`/`::after` for decoration:

```ts
chat.setTheme({
  // A gradient (or image) banner with white text, taller header, larger logo.
  customCss: `
    .sc-header {
      background: linear-gradient(120deg, #4f46e5, #9333ea);
      /* or a hosted banner image: */
      /* background: url("https://acme.com/widget-banner.png") center/cover; */
      --sc-header-text: #fff;
      min-height: 76px;
    }
    .sc-brand-mark { width: 36px; height: 36px; border-radius: 10px; background: #fff; }
    .sc-brand-mark img { object-fit: contain; padding: 4px; }
    .sc-header h1 { font-size: 17px; font-weight: 700; }
    .sc-iconbtn { color: #fff; }
  `,
});
```

**5 — Change the home greeting.** The home screen opens with a large greeting
(**"Hi there 👋"**) and a line of subtext (**"How can we help you today?"**).
Override the header title and both home lines from your install code with the
`content` option:

```js
SupportChat.createChat({
  workspaceId: "{YOUR_WORKSPACE_ID}",
  widgetId: "{YOUR_WIDGET_ID}",
  content: {
    headerTitle: "Acme Support",
    greeting: "Welcome back 👋",
    subtitle: "We usually reply in a few minutes.",
  },
});
```

Each `content` field is optional and, when unset, falls back to the built‑in
default — so you can override only the lines you care about.

| You want… | How |
|---|---|
| Different header text than "Support" | `content.headerTitle` |
| Different home greeting / subtext | `content.greeting` / `content.subtitle` |
| A logo in the header | `theme.logoUrl` |
| Solid header colors | `colors.headerBackground` / `colors.headerText` |
| A gradient / image **banner** | `customCss` → `.sc-header { background: … }` |
| Bigger / differently‑shaped logo | `customCss` → `.sc-brand-mark` / `.sc-brand-mark img` |
| Header font / weight | `theme.fontFamily` (whole widget) or `customCss` → `.sc-header h1` |

**What is fixed:** the **close** button (and the contextual **back** button on
sub‑pages) always render, and the **"Powered by Clad"** footer is permanent and
cannot be removed via theme or `customCss`. There is no separate header
*subtitle* field — add decorative copy with a `.sc-header::after { content: … }`
rule if you need it.

### Fonts

`theme.fontFamily` sets the `--sc-font` token used across the whole widget. Pass
any CSS font stack; the widget inherits whatever you list:

```ts
chat.setTheme({ fontFamily: '"Plus Jakarta Sans", system-ui, sans-serif' });
```

Because the UI runs in an isolated iframe, a **custom web font must be loaded
inside the iframe** — list a websafe/system stack, or load the font file via
`customCss`:

```ts
chat.setTheme({
  customCss: `
    @import url("https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600;700&display=swap");
  `,
  fontFamily: '"Plus Jakarta Sans", system-ui, sans-serif',
});
```

### Icons & iconography

- **Launcher icon:** the floating bubble shows a chat glyph by default. Swap it
  for your own mark with `launcher.iconUrl`, or recolor the default with
  `colors.launcher` (bubble) and `colors.launcherIcon` (glyph). For a fully
  custom launcher element of your own, see [Custom launcher](#custom-launcher)
  (`features.customLauncher` + render mode `custom-launcher`).
- **In‑widget icons** (search, send, the AI ✨ mark, chevrons) are line‑art SVGs
  that inherit `currentColor`, so they follow your text/primary tokens
  automatically. To restyle or replace one, target it via `customCss` (e.g.
  `.sc-launcher svg { … }`, `.sc-search-icon svg { … }`).

### Customizing which surfaces appear

Beyond colors, you control **which parts of the UI show** from code via the
[`features`](#feature-visibility-overrides-features) option — hide the knowledge
base search, ticket forms, or the conversation entry points per page. Combine
that with [render modes](#render-modes) (floating, embedded, or a custom
launcher) and the `launcher` options (position, label, icon) to fit the widget
into your app.

```js
window.SupportChatSettings = {
  workspaceId: "{YOUR_WORKSPACE_ID}",
  widgetId: "{YOUR_WIDGET_ID}",
  theme: { colors: { primary: "#0f766e" }, borderRadius: "md" }, // brand look
  features: { ticketForms: false },                              // hide a surface
  launcher: { position: "bottom-left", label: "Help" },          // launcher
};
```
