---
title: Kindly Android SDK — Theming Reference
slug: sdk/android-sdk-skill-theming
description: Complete reference for theming the Kindly Android SDK. Use when customizing Compose colors via setCustomTheme, building light/dark mode pairs, or working with KindlyThemeColors and CustomKindlyTheme. Lists every color slot, its API mapping, and which UI…
createdAt: 2026-11-04T11:39:56.807Z
---

Deep reference for every color slot in the Kindly Android chat UI (Jetpack Compose). Companion to [SKILL.md](https://docs.kindly.ai/sdk/android-sdk-skill.md) — use this when an agent needs full color-by-color control over the widget.

All types listed here live in `no.kindly.chatsdk.chat.presentation.theme`. Colors are `androidx.compose.ui.graphics.Color?`.

***

## Theme Resolution Order

For each color slot, the SDK picks the first non-`null` value from this precedence chain:

1. **Custom theme** set via `KindlySDK.setCustomTheme(...)` (host app)
2. **API/server theme** loaded via `kindly:load` event (Kindly admin panel)
3. **Default Compose theme** baked into the SDK

Pass `null` for any slot in `setCustomTheme(...)` to fall back to layer 2 or 3 for that slot only. This means partial overrides "just work".

***

## Applying a Theme

Two equivalent forms exposed on `KindlySDK`:

```kotlin
import androidx.compose.ui.graphics.Color

KindlySDK.setCustomTheme(
    background = Color.White,
    headerBackground = Color(0xFF1976D2),
    headerText = Color.White,
    userMessageBackground = Color(0xFF1976D2),
    userMessageText = Color.White,
)
```

```kotlin
import no.kindly.chatsdk.chat.presentation.theme.CustomKindlyTheme

val theme = CustomKindlyTheme(
    background = Color.White,
    headerBackground = Color(0xFF1976D2),
    headerText = Color.White,
    userMessageBackground = Color(0xFF1976D2),
    userMessageText = Color.White,
)
KindlySDK.setCustomTheme(theme)
```

Reset to the API/default theme:

```kotlin
KindlySDK.clearCustomTheme()
```

You can call `setCustomTheme` **before or after `start()`**; it bypasses SDK initialization checks (handled directly by `ThemeManager`).

***

## Color Slot Reference

The `KindlyThemeColors` interface defines two groups: **API-defined** (slots that mirror the Kindly admin panel theme) and **manually-defined** (mobile-only slots not covered by the web admin).

### API-defined slots

| Slot                          | Type     | API field                 | What it styles                                                        |
| ----------------------------- | -------- | ------------------------- | --------------------------------------------------------------------- |
| `background`                  | `Color?` | `background`              | Chat screen background                                                |
| `headerBackground`            | `Color?` | `header_background`       | Top app bar / header background                                       |
| `headerText`                  | `Color?` | `header_text`             | Header title and icons                                                |
| `botMessageBackground`        | `Color?` | `bot_message_background`  | Bot bubble background                                                 |
| `botMessageText`              | `Color?` | `bot_message_text`        | Bot bubble text                                                       |
| `userMessageBackground`       | `Color?` | `user_message_background` | User bubble background                                                |
| `userMessageText`             | `Color?` | `user_message_text`       | User bubble text                                                      |
| `buttonBackground`            | `Color?` | `button_background`       | Primary quick-reply / dialogue button background                      |
| `buttonText`                  | `Color?` | `button_text`             | Button label text                                                     |
| `buttonOutline`               | `Color?` | —                         | Button border color                                                   |
| `bubbleButtonBackground`      | `Color?` | —                         | Background for buttons rendered inside the chat bubble row            |
| `secondaryButtonBackground`   | `Color?` | —                         | Background for secondary action buttons                               |
| `globalIconsButtonBackground` | `Color?` | —                         | Background for global icon buttons (e.g. attach, send)                |
| `chatBubbleText`              | `Color?` | —                         | Text inside in-bubble action buttons                                  |
| `chatLogElements`             | `Color?` | —                         | Timestamps, dividers, "Bot is typing" indicator                       |
| `inputBackground`             | `Color?` | `input_background`        | Text input field background                                           |
| `inputText`                   | `Color?` | `input_text`              | Text input field text                                                 |
| `panelBackground`             | `Color?` | —                         | Background for surfaces like menus, sheets, the language picker panel |

### Manually-defined slots (Android-only)

These slots are not in the web admin panel and are only configurable from the mobile SDK.

| Slot                          | Type     | What it styles                                   |
| ----------------------------- | -------- | ------------------------------------------------ |
| `buttonSelectedBackground`    | `Color?` | Background for buttons after they've been tapped |
| `defaultShadow`               | `Color?` | Shadow color used on bubbles, headers, etc.      |
| `inputCursor`                 | `Color?` | Caret color in the text input field              |
| `maintenanceHeaderBackground` | `Color?` | Background of the "maintenance mode" banner      |

***

## Worked Examples

### Brand-aligned light theme

```kotlin
val brand = Color(0xFF1976D2)
KindlySDK.setCustomTheme(
    background = Color.White,
    botMessageBackground = Color(0xFFF5F5F5),
    botMessageText = Color.Black,
    buttonBackground = brand,
    buttonText = Color.White,
    headerBackground = brand,
    headerText = Color.White,
    userMessageBackground = brand,
    userMessageText = Color.White,
)
```

### Light/dark mode driven by your app's Compose theme

Compose has no "dynamic color" primitive like iOS, so resolve the pair manually based on your `MaterialTheme` / your own `isSystemInDarkTheme()`:

```kotlin
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect

@Composable
fun ApplyKindlyTheme() {
    val dark = isSystemInDarkTheme()
    LaunchedEffect(dark) {
        if (dark) {
            KindlySDK.setCustomTheme(
                background = Color(0xFF121212),
                botMessageBackground = Color(0xFF1F1F1F),
                botMessageText = Color.White,
                userMessageBackground = Color(0xFF1976D2),
                userMessageText = Color.White,
                inputBackground = Color(0xFF2A2A2A),
                inputText = Color.White,
                headerBackground = Color(0xFF1F1F1F),
                headerText = Color.White,
            )
        } else {
            KindlySDK.setCustomTheme(
                background = Color.White,
                botMessageBackground = Color(0xFFF5F5F5),
                botMessageText = Color.Black,
                userMessageBackground = Color(0xFF1976D2),
                userMessageText = Color.White,
                inputBackground = Color(0xFFF4F7FA),
                inputText = Color.Black,
                headerBackground = Color(0xFF1976D2),
                headerText = Color.White,
            )
        }
    }
}
```

### Match colors from your `colorScheme`

```kotlin
@Composable
fun MirrorMaterialTheme() {
    val cs = MaterialTheme.colorScheme
    LaunchedEffect(cs) {
        KindlySDK.setCustomTheme(
            background = cs.background,
            headerBackground = cs.primary,
            headerText = cs.onPrimary,
            userMessageBackground = cs.primary,
            userMessageText = cs.onPrimary,
            buttonBackground = cs.secondary,
            buttonText = cs.onSecondary,
        )
    }
}
```

### Implement a fully custom `KindlyThemeColors`

If you want every slot under your control instead of using `CustomKindlyTheme`'s fall-through behavior, implement the interface directly and pass it to `setCustomTheme(theme:)`:

```kotlin
class MyTheme : KindlyThemeColors {
    override val background = Color.White
    override val headerBackground = Color(0xFF1976D2)
    override val headerText = Color.White
    // ... implement every slot, return null to inherit ...
    override val botMessageBackground = null
    override val botMessageText = null
    // ...
}

KindlySDK.setCustomTheme(MyTheme())
```

Slots you return as `null` still fall through to the API/default chain.

### Read the active theme from a `kindly:load` event

The `KindlyEventStyle` payload (see [EVENTS.md](https://docs.kindly.ai/sdk/android-sdk-skill-events.md)) carries the API-side hex strings. Use it to detect what the admin configured before deciding which slots to override:

```kotlin
lifecycleScope.launch {
    KindlySDK.events
        .mapNotNull { (it.detail as? KindlyEventDetail.Load)?.settings?.style }
        .collect { apiStyle ->
            if (apiStyle.headerBackground == null) {
                KindlySDK.setCustomTheme(
                    headerBackground = Color(0xFF1976D2),
                    headerText = Color.White,
                )
            }
        }
}
```

***

## Notes & Gotchas

- `setCustomTheme` is safe to call **before** `start()` — the call delegates to `ThemeManager` which keeps state independent of SDK initialization.
- Calling `setCustomTheme` while the chat UI is on screen triggers Compose recomposition for most slots; some shadow-related slots only update on next layout.
- `clearCustomTheme()` does **not** clear the API theme — only the host-app override.
- Compose `Color` uses `0xAARRGGBB` ordering, e.g. `Color(0xFF1976D2)` is fully opaque blue. Use `Color(red, green, blue, alpha)` overload for floats.
