Theming

The Agentforce iOS SDK lets you customize the colors, fonts, icons, and on-screen text of every UI component in the full-UI chat experience, so the agent matches your brand.

If you’re using Agentforce Mobile Publisher, you can configure branding through the Setup UI without writing code. The metadata-driven branding configuration supports colors, typography, icons, and display names with a live preview. This Setup UI is available starting with the Winter ‘27 (264) release.

To configure branding via Setup:

  1. In Setup, search for Agentforce Mobile Branding
  2. Select your branding scope and preview type
  3. Customize colors, typography, icons, and display names
  4. Click the preview (clickable) to see your changes in real-time

Changes made in Setup are applied automatically without requiring an app redeployment—only server metadata updates are needed.

For programmatic customization in SDK implementations, continue reading the sections below.

This branding configuration applies to the Agentforce Mobile SDK and Agentforce Mobile Publisher. It doesn’t apply to the standalone Enhanced Chat (MIAW) SDK.

All theming types are public members of the AgentforceSDK module — import AgentforceSDK is enough; there’s no separate theming namespace.

The theming system is built on the Salesforce Cosmos design system and provides:

  • Color theming — a complete palette with generic foundation tokens plus a token for every individual component.
  • Typography — a theme-wide font family, plus per-style size, weight, and family overrides.
  • Icon theming — replace built-in icons, with optional separate light and dark variants.
  • Display-name theming — override user-visible strings such as titles, hints, and button labels.
  • Light and dark mode — automatic switching, or an explicitly forced mode.
  • Dynamic Type — fonts scale with the user’s text-size preference by default.
  • Reactive updates — the theme manager is @Observable, so SwiftUI re-renders when values change.
CapabilityType(s)
Colors (generic + per-component)AgentforceColorToken, AgentforceColorPalette
Font family (theme-wide)AgentforceThemeOverrides.fontFamilyName
Per-style typography (size, weight, family)AgentforceFontStyle, AgentforceFontOverride
IconsAgentforceIconType, AgentforceIcon
Display-name stringsAgentforceDisplayNameType
Theme mode (light, dark, system)AgentforceThemeMode
Dynamic Type on/offAgentforceDefaultThemeManager.dynamicFonts
TypePurpose
AgentforceThemingThe single theming input you pass to setTheming(_:): .default, .overrides, or .customManager.
AgentforceThemeOverridesA sparse bag of just the tokens you want to change (colors, fonts, icons, display names). The recommended way to theme.
AgentforceColorTokenType-safe enum of every color token — use it instead of raw strings so typos become compile errors.
AgentforceColorPaletteProtocol declaring every component color token. Refines SharedUI.Colors, which declares the generic tokens.
AgentforceCosmosLightColors / AgentforceCosmosDarkColorsThe default light and dark palettes. open classes — subclass to build a reusable palette.
AgentforceThemeManagerThe theme contract: AgentforceTheme & AgentforceThemeControllable & Observable.
AgentforceDefaultThemeManagerThe SDK’s default manager. open — subclass and override lightModeColors, darkModeColors, fixedSizeFonts, dynamicSizeFonts, icons, displayNames.
AgentforceThemeMode.light, .dark, or .system.
AgentforceFontStyle / AgentforceFontOverrideThe 23 overridable text styles and the sparse size, weight, or family override applied to each.
AgentforceIconType / AgentforceIconThe 23 customizable icon slots and the replacement image(s).
AgentforceDisplayNameTypeThe 45 customizable user-visible strings.

Provide your theme once, through setTheming(_:) on your agent configuration, using the AgentforceTheming enum:

OptionWhen to use
.overrides (recommended)Tweak specific tokens and let everything else follow the SDK defaults. Any token you leave out keeps its default.
.customManagerOwn the entire theme end-to-end with a fully resolved manager.
.defaultNo in-code theme — the SDK default theme is used.

setTheming(_:) is available on ServiceAgentConfiguration, EmployeeAgentConfiguration, and AgentforceConfiguration. It returns a copy, so chain it like any other builder method.

.overrides and .customManager are mutually exclusive: each call clears the other. You can’t layer an overrides bag on top of your own theme manager — if both end up set, the bag wins and the custom manager is dropped from the merge. The one exception is theme mode and Dynamic Type, which carry across (see Theme mode).

Colors can be overridden token by token, or built up into a reusable palette.

Use the .overrides factory with the AgentforceColorToken enum. List only what you want to change; everything else keeps its default.

Colors are plain SwiftUI Color values — the SDK doesn’t expose a hex initializer, so define your brand colors as asset-catalog colors (which can also carry their own light/dark variants) or with Color(red:green:blue:).

Most component tokens fall back to a generic token, so overriding a single generic token (like .accent1) re-skins everything that derives from it. To change just one component, override its specific token — for example .carouselSurface — without touching .surface1. Sixteen component tokens hold a literal rather than an alias and don’t follow a generic override; see the Color Token Reference for the full list and every token’s default value.

iOS has no withOverrides(...) helper. Instead, subclass the default palettes — every token is an open var, and unoverridden tokens keep resolving through their generic default.

Fonts are overridden per style. Each AgentforceFontOverride field is optional — a nil field falls through to the SDK default, so you can change only a size or only a weight.

fontFamilyName applies to every style that doesn’t name its own family. A style-level fontFamilyName wins over it. Register custom fonts in your app target (UIAppFonts in Info.plist) and pass the exact PostScript name.

A few things worth knowing before you ship a custom family:

  • Names aren’t validated. An unregistered or misspelled name is passed straight to Font.custom, which substitutes a fallback face silently — no crash, no log, just the wrong typeface. Verify on device.
  • CSS generic names don’t work here. "serif", "monospace", and "sans-serif" aren’t resolved as host values; pass a real family such as "Georgia" or "Menlo".
  • nil means inherit, not reset. Once a theme-wide family sets a field, passing nil at the style level keeps inheriting it. There’s no way to opt a single style back to the system font.
  • Fall-through is per field, not per style. Overriding just size on a style leaves that style’s weight at the SDK default — overriding one field doesn’t touch the others already set for that style.
  • Values are passed through as-is. A host size isn’t clamped, and fontFamilyName: "" counts as “set.” Pass nil for “no change.”
Style prefixCasesSize
bodyScaleNeg2*Regular, Semibold, Bold10 pt
bodyScaleNeg1*Regular, Semibold, Bold12 pt
bodyScaleBase*Regular, Semibold, Bold13 pt
bodyScale1*Regular, Semibold, Bold14 pt
bodyScale2*Regular, Semibold, Bold16 pt
titlesScale3*Regular, Semibold20 pt
titlesScale4*Regular, Semibold24 pt
titlesScale5Regular—28 pt
titlesScale6Regular—32 pt
displayScale7Light—40 pt
displayScale8Light—48 pt

AgentforceDefaultThemeManager(dynamicFonts:) selects between two palettes: AgentforceCosmosDynamicFonts (default, scales with the user’s text-size setting) and AgentforceCosmosFixedFonts (strictly fixed sizes). Leave it true unless you need pixel-fixed typography — Dynamic Type is an accessibility requirement (WCAG 1.4.4).

Replace any built-in icon by mapping an AgentforceIconType to an AgentforceIcon. An icon can be a single image (used for both schemes) or separate light and dark variants.

Icon slots. Avatars: aiAgent, humanAgent. Input actions: actionAttachFile, actionAttachPhoto, actionTakePhoto, actionSend, actionDownload, actionDelete, uiAdd, uiOptionsMenu. Status: statusError, statusFailed, statusWarning. Menu and content: iconForm, iconClearChat, iconSecureForm, iconWebLink, iconPDF, iconCopy, iconFeedbackPositive, iconFeedbackNegative, iconImagePlaceholder, iconClose.

Tinting. Most icon slots are recolored by the SDK using a *IconTint color token and rendered as templates, so supply a single-color glyph for those. Five slots render untinted and keep the original colors of the image you supply: aiAgent, humanAgent, statusError, iconWebLink, and iconPDF. There’s no host-settable “no tint” value on iOS.

Because several slots appear in more than one place, the tint token applied to your glyph depends on where it renders. iconClose, for instance, is tinted by titleBarIconTint in the title bar, modalSheetItemIconTint in a sheet, and mediaViewerCloseIconTint in the image viewer. Design replacement glyphs to read well against any of those.

Known gap. uiOptionsMenu is honored in the title bar and conversation-options menu, but the “more” glyph inside citations and toxicity notices still uses the built-in image and ignores the override.

Override user-visible strings by mapping an AgentforceDisplayNameType to your text.

Two tokens carry a runtime value and must keep their placeholder: typingIndicatorText and routingWaitResolvedMinutes (both %1$@). An override with an unsafe or mismatched format string is rejected and the SDK’s own template is used instead. An empty-string override is treated as “not customized” and falls back to the localized default.

These strings reach VoiceOver. Some views keep a separate accessibility string (the typing indicator and the launcher placeholder announce their own localized copy regardless of your override), but many don’t: the action-menu items, conversation-options items, participant status, queue wait-time copy, agent name, and chat title are all announced using the text you supply. Write overrides that make sense read aloud, and don’t rely on a display name being visual-only.

Theme mode lives on the theme manager, not in the overrides bag:

  • .system — follow the device setting.
  • .light / .dark — force a mode regardless of the device setting.

The SDK default is .light. Because mode isn’t part of the overrides bag, combining .system with per-token overrides takes two chained calls — seat a manager that carries the mode, then attach the overrides. setTheming(.overrides(...)) rebuilds a default manager but preserves the previous manager’s mode and Dynamic Type setting, so the mode survives:

On AgentforceConfiguration you can skip the chaining and pass themeManager: and themeOverrides: together in the initializer.

Order matters. Reverse the two calls and you lose everything, not just the mode — setting a theme manager after .overrides replaces the whole overrides bag with an empty one, silently, and you end up on .system with default colors. Always seat the manager first.

Two smaller behaviors to expect: in .system mode the theme reports light until the view first appears and syncs with the environment, and dynamicFonts is only carried across a setTheming call if your manager subclasses AgentforceDefaultThemeManager — any other conformer is treated as dynamicFonts == true.

Combine everything in a single overrides bag:

Or own the whole theme as a fully resolved manager:

Roll out a custom theme gradually by choosing the theming input at runtime:

If you render custom components through AgentforceViewProviding and want them to match the theme, hold the same AgentforceThemeManager instance you handed to the SDK. Because it’s @Observable, SwiftUI re-renders your views when the active scheme changes a value.

Alongside colors and fonts, a theme manager exposes dimensions (.space.spacing1–12, .size.sizing1–16, border widths) and shapes (corner radii) from the shared Salesforce token families.

  1. Prefer .overrides. Override only what you need and let the SDK supply the rest.
  2. Use generic tokens for broad changes, component tokens for surgical ones. Change .accent1 to re-brand everything accent-colored; change .carouselSurface to affect only the carousel.
  3. Always use the AgentforceColorToken enum rather than raw strings — typos become compile errors.
  4. Set both light and dark. Provide light and dark maps so your brand looks right in both modes; test with .system.
  5. Check contrast. Ensure sufficient contrast for accessibility (WCAG AA) in both modes.
  6. Keep Dynamic Type on unless you have a hard requirement for fixed sizing.
  7. Preserve string placeholders (%1$@) when overriding the two runtime-format display names.
  8. Supply single-color glyphs for tinted icon slots; avatars, statusError, iconWebLink, and iconPDF can be full-color.
  9. Read your display-name overrides aloud. Most of them are announced by VoiceOver verbatim.
  10. Verify custom fonts on device. An unregistered family name fails silently and substitutes a fallback face.

When branding is saved for an Embedded Service configuration — for example, through the Mobile Publisher Branding Setup UI described above — the SDK creates a BrandingSet metadata record named:

AFM_ESC_<EmbeddedServiceConfigDeveloperName>

For example, an Embedded Service configuration with the developer name MyMessagingConfig creates the BrandingSet AFM_ESC_MyMessagingConfig.

BrandingSet doesn’t have a parent metadata entity, so it can’t be included in a change set. To promote this branding from a sandbox to production, use Salesforce DevOps Center instead:

  1. Create or open a DevOps Center work item for the Embedded Service configuration.
  2. On the work item’s Changes tab, select Add Components Manually.
  3. Select the BrandingSet metadata type and add the BrandingSet named AFM_ESC_<EmbeddedServiceConfigDeveloperName>.
  4. Commit the work item to source control, complete any required review, and promote it through the configured pipeline to production.
  5. Validate the branding in the target org after deployment.

When the Embedded Service configuration itself changes, include both the configuration and its associated BrandingSet in the same work item.

This limitation and workaround apply the same way regardless of mobile platform — the BrandingSet metadata is created server-side, independent of whether your app uses the Android, iOS, or React Native SDK.

For more information, see Salesforce’s DevOps Center documentation on manually adding metadata components, committing work-item changes, and promoting work items through a pipeline.