All colors in FioriThemeManager are dynamic colors. They automatically adapt to the iOS appearance setting (light or dark) without any extra work on your part. The key is to always use Color.preferredColor(_:) rather than hardcoded color values.
Never use hardcoded hex values or system colors like Color.black in Fiori components. Always use Color.preferredColor(_:) so your UI adapts correctly to light mode, dark mode, and high-contrast accessibility settings.
Basic usage
import FioriThemeManager
// Returns a dynamic color that adapts to the current appearance
let labelColor = Color.preferredColor(.primaryLabel)
let backgroundFill = Color.preferredColor(.primaryBackground)
The returned Color is a dynamic color provider — it resolves to the correct variant at render time based on the current environment.
Controlling the background color scheme
By default, Color.preferredColor(_:) follows the device appearance (BackgroundColorScheme.device). You can override this with the background parameter.
// Follows the device appearance setting (default)
Color.preferredColor(.primaryLabel, background: .device)
// Always uses the dark-background variant, regardless of device setting
// (foreground colors will be light variants)
Color.preferredColor(.primaryLabel, background: .darkConstant)
// Always uses the light-background variant, regardless of device setting
// (foreground colors will be dark variants)
Color.preferredColor(.primaryLabel, background: .lightConstant)
// Inverts the device setting — light device becomes dark, and vice versa
Color.preferredColor(.primaryLabel, background: .deviceInverse)
BackgroundColorScheme describes the background color environment. The foreground color variant is chosen to contrast against it:
| Value | Behavior |
|---|
.device | Follows system appearance (default) |
.deviceInverse | Inverts the system appearance |
.lightConstant | Always uses dark foreground variants (for light backgrounds) |
.darkConstant | Always uses light foreground variants (for dark backgrounds) |
.light and .dark are deprecated aliases for .lightConstant and .darkConstant. Use the constant-suffixed names in new code.
Controlling contrast with ColorDisplayMode
The display parameter controls how the color’s contrast level is resolved relative to the device’s accessibility contrast setting.
// Follows the device contrast setting (default)
Color.preferredColor(.primaryLabel, display: .device)
// Inverts the device contrast setting
Color.preferredColor(.primaryLabel, display: .deviceInverse)
// Always normal contrast, regardless of accessibility settings
Color.preferredColor(.primaryLabel, display: .normalConstant)
// Always high contrast, regardless of accessibility settings
Color.preferredColor(.primaryLabel, display: .highConstant)
| Value | Behavior |
|---|
.device | Follows device accessibility contrast setting (default) |
.deviceInverse | Inverts the device contrast setting |
.normalConstant | Forces normal contrast |
.highConstant | Forces high contrast |
Interface level for popovers and alerts
Use the interface parameter to specify whether content appears at the base level (main window) or an elevated level (popover, alert, sheet). iOS applies a subtle background difference between the two.
// Follows the device interface level (default)
Color.preferredColor(.primaryBackground, interface: .device)
// Use for main window content
Color.preferredColor(.primaryBackground, interface: .baseConstant)
// Use for popovers, alerts, and sheets
Color.preferredColor(.primaryBackground, interface: .elevatedConstant)
| Value | Behavior |
|---|
.device | Follows device interface level (default) |
.deviceInverse | Inverts the device interface level |
.baseConstant | Main window content |
.elevatedConstant | Elevated surfaces (popovers, alerts, sheets) |
Combining parameters
All three parameters can be combined:
// High-contrast color for an elevated surface on a dark background
Color.preferredColor(
.primaryLabel,
background: .darkConstant,
interface: .elevatedConstant,
display: .highConstant
)
Resolving a static color
If you need a fixed Color value at a specific point in time — for example, to pass to a UIKit API — use resolvedColor(with:in:) to snapshot the dynamic color:
let dynamicColor = Color.preferredColor(.primaryLabel)
// Resolve to a static color for the light appearance at base level
let staticLight = dynamicColor.resolvedColor(with: .light, in: .base)
// Resolve to a static color for the dark appearance at elevated level
let staticDark = dynamicColor.resolvedColor(with: .dark, in: .unspecified)
resolvedColor(with:in:) is only available on iOS and visionOS.
Customizing palette colors
To override a color style for your application, use ThemeManager.shared early in the app lifecycle:
// In AppDelegate.application(_:didFinishLaunchingWithOptions:)
// Pin to a specific palette version
ThemeManager.shared.setPaletteVersion(.v8)
// Override a specific color style
ThemeManager.shared.setColor(.darkGray, for: .secondaryLabel, variant: .light)
ThemeManager.shared.setHexColor("1b931d", for: .positiveLabel, variant: .light)
Most components do not reload dynamically after the theme is set. Call ThemeManager.shared methods in application(_:didFinishLaunchingWithOptions:), before any UI is displayed.