Kindly Android SDK — Theming Reference
Deep reference for every color slot in the Kindly Android chat UI (Jetpack Compose). Companion to 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:
- Custom theme set via KindlySDK.setCustomTheme(...) (host app)
- API/server theme loaded via kindly:load event (Kindly admin panel)
- 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:
import androidx.compose.ui.graphics.Color
KindlySDK.setCustomTheme(
background = Color.White,
headerBackground = Color(0xFF1976D2),
headerText = Color.White,
userMessageBackground = Color(0xFF1976D2),
userMessageText = Color.White,
)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:
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
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():
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
@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:):
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) carries the API-side hex strings. Use it to detect what the admin configured before deciding which slots to override:
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.