Kindly iOS SDK — Theming Reference
Deep reference for every color slot in the Kindly iOS chat UI. Companion to SKILL.md — use this when an agent needs full color-by-color control over the widget.
All types listed here live in the Kindly module. Theme colors are UIColor?.
Theme Resolution Order
For each color slot, the SDK picks the first non-nil 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 KindlyTheme baked into the SDK
Pass nil 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:
KindlySDK.setCustomTheme(
background: .white,
navBarBackground: .systemBlue,
navBarText: .white,
userMessageBackground: .systemBlue,
userMessageText: .white
// any unset color falls through to API/default
)let theme = CustomTheme(
background: .white,
navBarBackground: .systemBlue,
navBarText: .white,
userMessageBackground: .systemBlue,
userMessageText: .white
)
KindlySDK.setCustomTheme(theme)Reset to the API/default theme:
KindlySDK.clearCustomTheme()You can call setCustomTheme before or after start(); it does not require an active connection.
Color Slot Reference
The Theme protocol 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 | UIColor? | background | Chat view background |
navBarBackground | UIColor? | header_background | Navigation bar / chat header background |
navBarText | UIColor? | header_text | Navigation bar title and icons |
botMessageBackground | UIColor? | bot_message_background | Bot bubble background |
botMessageText | UIColor? | bot_message_text | Bot bubble text |
userMessageBackground | UIColor? | user_message_background | User bubble background |
userMessageText | UIColor? | user_message_text | User bubble text |
buttonBackground | UIColor? | button_background | Quick-reply / dialogue button background |
buttonText | UIColor? | button_text | Button label text |
buttonOutline | UIColor? | — | Button border color (iOS-only render hint) |
inputBackground | UIColor? | input_background | Text input field background |
inputText | UIColor? | input_text | Text input field text |
chatLogElements | UIColor? | — | Timestamps, dividers, "Bot is typing" indicator |
Manually-defined slots (iOS-only)
These slots are not in the web admin panel and are only configurable from the mobile SDK.
Slot | Type | What it styles |
|---|---|---|
buttonSelectedBackground | UIColor? | Background for buttons after they've been tapped |
defaultShadow | UIColor? | Shadow color used on bubbles, headers, etc. |
inputCursor | UIColor? | Caret color in the text input field |
maintenanceHeaderBackground | UIColor? | Background of the "maintenance mode" banner |
KindlyTheme — the built-in default
These are the fallbacks the SDK uses when no API or custom value is set:
Slot | Default |
|---|---|
background | #FFFFFF |
botMessageBackground | #F1F4F8 |
botMessageText | #232F41 |
buttonBackground | #FFFFFF |
buttonOutline | #000000 @ 8% alpha |
buttonText | #000000 |
chatLogElements | #727272 |
navBarBackground | #445265 |
navBarText | #FFFFFF |
inputBackground | #F4F7FA |
inputText | #0C141F |
userMessageBackground | #0069FF |
userMessageText | #FFFFFF |
buttonSelectedBackground | #000000 @ 18% alpha |
defaultShadow | black @ 8% alpha |
inputCursor | #000000 |
maintenanceHeaderBackground | #F9EF94 |
Worked Examples
Brand-aligned light theme
let brand = UIColor(red: 0.10, green: 0.45, blue: 0.95, alpha: 1)
KindlySDK.setCustomTheme(
background: .white,
botMessageBackground: UIColor(white: 0.96, alpha: 1),
botMessageText: .black,
buttonBackground: brand,
buttonText: .white,
navBarBackground: brand,
navBarText: .white,
userMessageBackground: brand,
userMessageText: .white
)Light/dark mode via UIColor dynamic providers
UIColor already supports trait-collection-based dynamic colors — use them so the theme follows system appearance automatically:
extension UIColor {
static func dynamic(light: UIColor, dark: UIColor) -> UIColor {
UIColor { trait in trait.userInterfaceStyle == .dark ? dark : light }
}
}
KindlySDK.setCustomTheme(
background: .dynamic(light: .white, dark: UIColor(white: 0.10, alpha: 1)),
botMessageBackground: .dynamic(light: UIColor(white: 0.96, alpha: 1),
dark: UIColor(white: 0.18, alpha: 1)),
botMessageText: .dynamic(light: .black, dark: .white),
userMessageBackground: .systemBlue,
userMessageText: .white,
inputBackground: .dynamic(light: UIColor(white: 0.95, alpha: 1),
dark: UIColor(white: 0.20, alpha: 1)),
inputText: .dynamic(light: .black, dark: .white)
)Match an asset catalog color
KindlySDK.setCustomTheme(
navBarBackground: UIColor(named: "BrandPrimary"),
userMessageBackground: UIColor(named: "BrandPrimary"),
userMessageText: UIColor(named: "BrandPrimaryOn")
)Implement a fully custom Theme
If you want every slot under your control instead of using CustomTheme's fall-through behavior, implement the Theme protocol directly and pass it to setCustomTheme(_:):
struct MyTheme: Theme {
var background: UIColor? { .white }
var navBarBackground: UIColor? { .systemBlue }
// ... implement every slot ...
}
KindlySDK.setCustomTheme(MyTheme())Slots you return as nil 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:
KindlySDK.events
.compactMap { event -> KindlyEventStyle? in
if case .load(let settings) = event.detail { return settings.style }
return nil
}
.sink { apiStyle in
if apiStyle.headerBackground == nil {
KindlySDK.setCustomTheme(navBarBackground: .systemBlue, navBarText: .white)
}
}
.store(in: &cancellables)Notes & Gotchas
- setCustomTheme is safe to call before start() — the SDK stores the theme and applies it as soon as the UI mounts.
- Calling setCustomTheme while the chat UI is on screen takes effect immediately for most slots; some slots (e.g. defaultShadow) only re-render on next layout.
- clearCustomTheme() does not clear the API theme — only the host-app override. To force the built-in KindlyTheme, you'd need both no API style and no custom theme.
- UIColor(hex:) is an internal SDK extension — use the standard UIColor(red:green:blue:alpha:) or asset-catalog colors in host code.