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

# Appearance & config

> Style and configure the widget from the agent's Appearance and Embed tabs - colors, type, layout, icons, display modes, transcript detail, approvals, origins, and pre-chat consent.

The widget's look and behavior are set on the agent's **Appearance** tab and
delivered at runtime, so your hosted page and every embed key inherit the same
appearance, live, with no change to the script tag on your site. A small set of
visual basics can also be overridden inline on the script tag.

## Where config comes from

| Source                       | Sets                                                                                                     | Scope                                                         |
| ---------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| **Appearance tab**           | The full theme (colors, type, layout, icons) and behavior (display modes, transcript detail, approvals). | The agent - inherited by the hosted page and every embed key. |
| **Embed tab**                | The publishable key, allowed origins, anonymous spend cap, and identity secret.                          | Per embed key.                                                |
| **Inline `data-*` / `init`** | A subset of the visual basics.                                                                           | The one script tag.                                           |

Config is merged in this precedence (later wins):

```
built-in defaults  <  Appearance tab  <  data-* attributes  <  init overrides
```

The displayed identity is the exception. A named Karta always uses its Karta
name; otherwise the widget uses the underlying agent's name. Appearance and
inline theme values cannot rename that server-resolved identity.

The richer color, type, layout, and icon fields, and all
[behavior](#behavior), come from the Appearance tab only; the inline path covers
the basics under [Inline overrides](#inline-overrides). If the runtime config
fetch is unavailable, the widget falls back to defaults + `data-*` and never
throws into your page.

## Theme presets

Start from a preset on the **Appearance** tab - **Karta** (the default),
**Moss**, or **Cobalt**. Each preset is a complete look (a full color set plus
type and layout), so selecting one fills every field below. Override individual
fields afterward to fine-tune, or keep the preset as is.

## Theme fields

Set these on the **Appearance** tab. The fields marked *(inline)* can also be set
with `data-theme-*` on the script tag (camelCase becomes kebab-case) or in
`karta("init", { theme: {...} })`.

**Colors**

| Field              | Controls                                             |
| ------------------ | ---------------------------------------------------- |
| `accent`           | Primary color - launcher, buttons, links. *(inline)* |
| `accentForeground` | Text and icon color on the accent. *(inline)*        |
| `background`       | The page frame behind the widget.                    |
| `surface`          | The widget's own surface.                            |
| `text`             | Body text.                                           |
| `border`           | Lines and dividers.                                  |
| `userBubble`       | The end user's message bubble.                       |
| `userInk`          | Text inside the user's bubble.                       |
| `fieldBg`          | The composer (input) background.                     |
| `fieldInk`         | The composer text.                                   |

**Shape & type**

| Field         | Controls                                                                                                                                         |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `radius`      | Message corner radius (e.g. `12px`).                                                                                                             |
| `fontFamily`  | The widget's body font stack.                                                                                                                    |
| `headingFont` | Font stack for headings in the agent's replies.                                                                                                  |
| `monoFont`    | Font stack for code and other monospace text. Defaults to the bundled Karta Mono family.                                                         |
| `fontSize`    | Base text size. Scales the whole widget, including the agent's markdown.                                                                         |
| `leading`     | Line height of the agent's markdown text.                                                                                                        |
| `flow`        | Paragraph spacing in the agent's markdown text.                                                                                                  |
| `maxWidth`    | Maximum width of the chat thread.                                                                                                                |
| `colorScheme` | `light`, `dark`, or `auto` - a quick way to get a dark default without setting every color. Once you set colors explicitly, they win. *(inline)* |

**Layout**

| Field          | Controls                                  |
| -------------- | ----------------------------------------- |
| `messageStyle` | `bubbles` or `flat`.                      |
| `composer`     | Composer shape - `pill` or `boxed`.       |
| `density`      | `comfortable` or `compact`.               |
| `showAvatars`  | Show the agent avatar beside its replies. |

**Content & chrome**

| Field              | `data-theme-*`                 | Controls                                                                          |
| ------------------ | ------------------------------ | --------------------------------------------------------------------------------- |
| `agentName`        | `data-theme-agent-name`        | Fail-soft title fallback when runtime identity config is unavailable. *(inline)*  |
| `agentAvatar`      | `data-theme-agent-avatar`      | URL or emoji. *(inline)*                                                          |
| `workingIndicator` | -                              | Active-turn animation style, size, speed, and optional custom animated-image URL. |
| `greeting`         | `data-theme-greeting`          | First-open agent message. *(inline)*                                              |
| `suggestedPrompts` | `data-theme-suggested-prompts` | Pipe-separated starter prompts. *(inline)*                                        |
| `launcherIcon`     | `data-theme-launcher-icon`     | Emoji or glyph in the launcher bubble. *(inline)*                                 |
| `position`         | `data-theme-position`          | `bottom-right` (default) or `bottom-left`. *(inline)*                             |
| `showPoweredBy`    | `data-theme-powered-by`        | `false` hides the footer. *(inline)*                                              |

**Icons.** Each chrome glyph is customizable on the Appearance tab: `iconSend`,
`iconLauncher`, `iconExpand`, `iconCollapse` (shrink), `iconClose`, and
`iconMenu`. Each falls back to a default when unset.

## Thinking animation

The active-turn footer can use:

* The animated **Karta mark**.
* Six Karta activity motions: **Reading**, **Coding**, **Executing**,
  **Connecting**, **Coordinating**, and **Building**.
* Six tuned orb styles: **Working**, **Searching**, **Solving**, **Listening**,
  **Composing**, and **Shaping**.
* A custom animated image.

These are visual branding choices rather than inferred agent state. Selecting
Coding does not claim that the current activity is editing a file; the
transcript's activity rows remain the source of truth.

```js theme={null}
karta("init", {
  theme: {
    workingIndicator: {
      type: "coding",
      size: 20,
      speed: 1,
    },
  },
});
```

The dashboard clamps size to `16`-`32` CSS pixels and speed to `0.5`-`2`. Set
`type: "custom"` and `customUrl` to use an HTTP(S) animated GIF, WebP, or APNG;
the asset controls its own playback speed. Invalid or failed custom assets fall
back to the Karta mark.

Animation is deliberately limited to a live foreground turn. Completed turns
do not animate, and a finished turn with background tasks uses a static mark.
With `prefers-reduced-motion: reduce`, every style (including custom animated
images) becomes a static Karta mark.

## Inline overrides

For a quick change without the dashboard, set the basics on the script tag.
Inline `data-theme-*` attributes cover `accent`, `accentForeground`, `position`,
`colorScheme`, `launcherIcon`, `agentName`, `agentAvatar`, `greeting`,
`suggestedPrompts`, and `showPoweredBy` (plus [consent](#pre-chat-consent)); the
`init` call accepts the same keys.

```html theme={null}
<script
  async
  src="https://cdn.karta.sh/widget/v1/karta.js"
  data-embed-key="pk_live_xxx"
  data-agent="coffeeco/support-bot"
  data-theme-accent="#c6f135"
  data-theme-greeting="Hi! How can I help?"
  data-theme-suggested-prompts="Track my order | Returns | Brewing tips"
></script>
```

The richer color, layout, and icon fields, and all [behavior](#behavior), are set
on the Appearance tab - they are not inline-overridable.

## Behavior

Behavior is configured on the **Appearance** tab; it has no inline override.

* **Display modes.** Whether the widget opens as a **popup**, **full-size**, or
  **both** (the visitor can expand a popup to full-size). The hosted chat page
  renders in the configured mode.
* **Transcript detail.** Show or hide the agent's **reasoning**, and choose how
  its **tool activity** appears: compact status rows, full details, or hidden.
  When tool activity is shown, you can independently include the raw tool input
  and the raw tool output. Final answers always show, so hiding these gives end
  users an answer-only view.
* **Sessions.** Whether the widget reopens the visitor's newest conversation
  when it loads (on by default). The resume runs only when the chat panel first
  opens, so a page view alone starts nothing. Turn it off to give every load a
  fresh conversation. Continuity follows the
  [end-user identity](/sdks/widget/identity) your page provides (soft or
  verified); a visitor with no identity starts fresh.
* **Approvals.** Show or hide the **tool-approval prompt**. When shown (the
  default), the visitor gets an Allow / Deny prompt each time the agent asks to
  run a tool that requires permission. When hidden, the visitor is not prompted:
  a tool that requires approval ends the turn with a brief notice after the
  hidden-approval timeout (default 30s). Hiding the prompt suppresses the
  question; it does not grant the tool. To let a tool run without prompting,
  pre-authorize it in the agent's [permissions](/build/karta-toml) and
  redeploy.

## The embed key

Each `pk_live_...` key is managed on the **Embed** tab. The key ships in your
page's HTML; its blast radius is fenced by configuration, not secrecy.

| Field                            | What it controls                                                                                                                                    |
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Embed key** (`pk_live_...`)    | The publishable key in your script tag. Shown in full once at creation; rotate to get a new value.                                                  |
| **Allowed origins**              | The scheme+host+port allowlist. A browser request from an origin not on the list is refused at token mint. No wildcards.                            |
| **Anonymous spend cap**          | A hard ceiling on what anonymous (non-verified) traffic on this key can cost per period. On hit, the widget shows a friendly `limit_reached` state. |
| **Identity-verification secret** | The per-key HMAC secret for [verified identity](/sdks/widget/identity). Generate / rotate / reveal-once / clear here.                               |
| **Enable / disable**             | Reversibly turn the key off without deleting it. A disabled key mints no tokens.                                                                    |
| **Rotate / revoke**              | Replace the key value (other config carries over), or permanently retire it.                                                                        |

At mount the loader fetches the key's appearance and limits from the runtime
origin, gated by the browser's real `Origin`. It returns the theme and feature
flags only - never the allowed-origins list - so you can recolor the widget or
change the greeting from the dashboard and it takes effect on the next load,
with no code change on your site.

## Pre-chat consent

Show an optional **pre-chat disclosure** before the first message - for example,
to tell users the chat is AI-assisted and link your privacy policy. Configure it
in the theme's `consent` object, or inline on the script tag.

```html theme={null}
<script
  async
  src="https://cdn.karta.sh/widget/v1/karta.js"
  data-embed-key="pk_live_xxx"
  data-agent="coffeeco/support-bot"
  data-theme-consent="true"
  data-theme-consent-text="This chat is AI-assisted. See our [privacy policy](https://example.com/privacy)."
  data-theme-consent-require-accept="true"
></script>
```

or via the theme object:

```js theme={null}
karta("init", {
  theme: {
    consent: {
      enabled: true,
      text: "This chat is AI-assisted. See our [privacy policy](https://example.com/privacy).",
      requireAccept: true,
    },
  },
});
```

| Option          | Effect                                                                                                                                                                                                                                                              |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `enabled`       | Show the disclosure above the composer. Default `false`.                                                                                                                                                                                                            |
| `text`          | The disclosure markdown. Rendered through an escape-first sanitizer - only basic formatting and `http`/`https`/`mailto` links survive, so a privacy-policy link is safe and no executable markup can reach the page. Defaults to a short generic line when omitted. |
| `requireAccept` | Show an **Accept** button that disables the composer until the visitor clicks it. Acceptance is remembered per visitor, so it is not re-shown on every reload.                                                                                                      |

The disclosure is associated with the composer for screen readers
(`aria-describedby`). See
[Security & privacy](/security/chat-widget#privacy--data-path) for what you
should disclose.

## Localization

Override the widget's built-in strings with a `locale` map on the theme. Every
key is optional and falls back to its English default, so you can localize only
the strings you need.

```js theme={null}
karta("init", {
  theme: {
    locale: {
      inputPlaceholder: "Ecrivez un message...",
      sendLabel: "Envoyer",
      closeLabel: "Fermer",
      poweredBy: "Propulse par Karta",
    },
  },
});
```

| Key                | Localizes                                                                    |
| ------------------ | ---------------------------------------------------------------------------- |
| `inputPlaceholder` | The composer placeholder.                                                    |
| `sendLabel`        | The send button's accessible label.                                          |
| `closeLabel`       | The close button's accessible label.                                         |
| `dialogLabel`      | The chat panel's accessible name (`{name}` is replaced with the agent name). |
| `messageListLabel` | The transcript's accessible name.                                            |
| `consentAccept`    | The pre-chat consent Accept button.                                          |
| `poweredBy`        | The "Powered by Karta" footer.                                               |

## Next

<CardGroup cols={2}>
  <Card title="Command API & events" icon="terminal" href="/sdks/widget/command-api">
    Drive the configured widget from your page.
  </Card>

  <Card title="Security & privacy" icon="shield-halved" href="/security/chat-widget">
    Origin allowlist, spend cap, and the data path in depth.
  </Card>
</CardGroup>
