Styling & Theming

iOS SDK: Styling & Theming

Customize colors, typography, spacing, and component styles throughout the Digital Card Engine UI SDK using the centralized StyleConfiguration system.

Prerequisites:

  • SDK initialized with DigitalCardEngine
  • Access to the sdk.styleConfiguration instance

StyleConfiguration Overview

The SDK uses a single StyleConfiguration object that controls the visual appearance of all UI components. This matches the Android SDK design for cross-platform consistency.

let sdk = try DigitalCardEngine(/* ... */)

// After initialization, customize appearance
sdk.styleConfiguration.palette.secondary = Color(hex: 0xFF5722)
sdk.styleConfiguration.spacing = 12

Palette (Color System)

The Palette defines semantic colors used throughout the SDK. All colors follow Material Design 3 naming conventions and match Android exactly.

public struct Palette {
    public var surface: Color = .init(hex: 0xffffff)              // Backgrounds
    public var onSurface: Color = .init(hex: 0x1C1B1B)            // Primary text
    public var onSurfaceVariant: Color = .init(hex: 0x5C5F66)     // Secondary text
    public var primary: Color = .init(hex: 0x0019BF)              // Primary brand
    public var secondary: Color = .init(hex: 0x2353C8)            // Secondary brand/accent
    public var success: Color = .init(hex: 0x1A7F37)              // Success/positive
    public var outlineVariant: Color = .init(hex: 0xE4E6EB)       // Borders/dividers
    public var surfaceContainerHighest: Color = .init(hex: 0xE0E3E8) // Subtle backgrounds
    public var surfaceContainerLow: Color = .init(hex: 0xF3F4F7)  // Light backgrounds
}

Color Usage Guide

ColorDefaultUsage
surface#FFFFFFCard backgrounds, container surfaces
onSurface#1C1B1BPrimary text (titles, card labels)
onSurfaceVariant#5C5F66Secondary text (subtitles, metadata)
primary#0019BFPrimary brand elements
secondary#2353C8Action buttons, brand elements
success#1A7F37Positive amounts, success indicators
outlineVariant#E4E6EBDividers, borders, separators
surfaceContainerHighest#E0E3E8Card backgrounds when stacked
surfaceContainerLow#F3F4F7Light container backgrounds

Customizing Colors

// Apply your brand colors
sdk.styleConfiguration.palette = Palette(
    surface: Color.white,
    onSurface: Color(hex: 0x1A1A1A),
    onSurfaceVariant: Color(hex: 0x666666),
    primary: Color(hex: 0x0019BF),
    secondary: Color(hex: 0x00A86B),        // Your brand color
    success: Color(hex: 0x1A7F37),
    outlineVariant: Color(hex: 0xE0E0E0),
    surfaceContainerHighest: Color(hex: 0xF5F5F5),
    surfaceContainerLow: Color(hex: 0xF9F9F9)
)

Typography (Fonts & TypeScale)

The SDK uses Material Design 3 typography scales. Each scale has a regular and emphasized (bold) variant.

public struct Fonts {
    // Title
    var titleLarge: TypeScale
    var titleMedium: TypeScale
    var titleSmall: TypeScale
    
    // Label
    var labelLarge: TypeScale
    var labelMedium: TypeScale
    var labelSmall: TypeScale
    
    // Body
    var bodyLarge: TypeScale
    var bodyMedium: TypeScale
    var bodySmall: TypeScale
}

public struct TypeScale {
    let font: Font              // Regular weight
    let fontEmphasized: Font    // Bold/emphasized weight
    let tracking: CGFloat       // Letter spacing
}

Typography Scale Reference

ScaleSizeTrackingUsage
titleLarge22pt0ptScreen titles, large headings
titleMedium16pt0.15ptSection titles, card titles
titleSmall14pt0.1ptSmall headings, labels
labelLarge14pt0.1ptButton text, prominent labels
labelMedium12pt0.5ptInput labels, metadata
labelSmall11pt0.5ptCaptions, small metadata
bodyLarge16pt0.5ptPrimary body text
bodyMedium14pt0.25ptSecondary body text
bodySmall12pt0.4ptSmall body text, footnotes

Default Font Configuration

public struct Fonts {
    var titleLarge: TypeScale = .init(
        font: .montserratRegular(size: 22),
        fontEmphasized: .montserratMedium(size: 22),
        tracking: 0
    )
    
    var titleMedium: TypeScale = .init(
        font: .montserratMedium(size: 16),
        fontEmphasized: .montserratBold(size: 16),
        tracking: 0.15
    )
    
    var bodyMedium: TypeScale = .init(
        font: .montserratRegular(size: 14),
        fontEmphasized: .montserratMedium(size: 14),
        tracking: 0.25
    )
    
    // ... other scales
}

Custom Typography

import SwiftUI

// Define custom fonts
extension Font {
    static func myCustomRegular(size: CGFloat) -> Font {
        .custom("MyFont-Regular", size: size)
    }
    
    static func myCustomBold(size: CGFloat) -> Font {
        .custom("MyFont-Bold", size: size)
    }
}

// Apply custom fonts
sdk.styleConfiguration.fonts.bodyMedium = TypeScale(
    font: .myCustomRegular(size: 14),
    fontEmphasized: .myCustomBold(size: 14),
    tracking: 0.25
)

sdk.styleConfiguration.fonts.titleLarge = TypeScale(
    font: .myCustomRegular(size: 22),
    fontEmphasized: .myCustomBold(size: 22),
    tracking: 0
)

Component-Specific Properties

CardListViewProperties

Control card list behavior and layout.

public struct CardListViewProperties {
    public var title: Bool = true
    public var actionButton: Bool = true
    public var dividers: Bool = true
    public var cardListLayout: CardListViewLayout = .largeLayout
}

public enum CardListViewLayout {
    case defaultLayout  // Compact rows for dashboards
    case largeLayout    // Spacious cards (default)
}

Usage:

sdk.styleConfiguration.cardListViewProperties = CardListViewProperties(
    title: false,
    actionButton: true,
    dividers: true,
    cardListLayout: .defaultLayout
)

CardDetailsViewProperties

Control card details screen behavior.

public struct CardDetailsViewProperties {
    public var actionButton: Bool = true
    public var slider: Bool = false
    public var cardInfoLayout: CardDetailsViewLayout = .detailLayout
}

public enum CardDetailsViewLayout {
    case briefLayout   // Compact info
    case detailLayout  // Full details (default)
}

Usage:

sdk.styleConfiguration.cardDetailsViewProperties = CardDetailsViewProperties(
    actionButton: true,
    slider: true,
    cardInfoLayout: .detailLayout
)

TransactionHistoryViewProperties

Control transaction history display.

public struct TransactionHistoryViewProperties {
    public var leadingElement: TransactionHistoryLeadingElement = .icon
    public var groupByMonth: Bool = true
    public var timestampFormat: String = "dd/MM/yyyy HH:mm"
    public var filter: Bool = true
    public var numberOfTransactions: Int = 5
}

public enum TransactionHistoryLeadingElement {
    case none
    case icon
    case brandLogo
}

Usage:

sdk.styleConfiguration.transactionHistoryViewProperties = TransactionHistoryViewProperties(
    leadingElement: .icon,
    groupByMonth: true,
    timestampFormat: "MMM dd, yyyy",
    filter: true,
    numberOfTransactions: 10
)

Spacing

Global spacing multiplier applied to paddings and gaps throughout the SDK.

// Default is 8pt
sdk.styleConfiguration.spacing = 12  // Increase spacing
sdk.styleConfiguration.spacing = 4   // Decrease spacing

Style Properties

CardListViewStyle

public struct CardListViewStyle {
    public var internalHorizontalPadding: CGFloat = 16
    public var internalVerticalPadding: CGFloat = 12
    public var externalPadding: CGFloat = 16
    public var internalGap: CGFloat = 16
    public var radius: CGFloat = 12
}

CardDetailsViewStyle

public struct CardDetailsViewStyle {
    public let internalHorizontalPadding: CGFloat = 16
    public let internalVerticalPadding: CGFloat = 12
    public let externalPadding: CGFloat = 16
    public let internalGap: CGFloat = 16
    public let radius: CGFloat = 12
}

TransactionHistoryViewStyle

public struct TransactionHistoryViewStyle {
    public var internalHorizontalPadding: CGFloat = 16
    public var internalVerticalPadding: CGFloat = 12
    public var externalPadding: CGFloat = 16
    public var internalGap: CGFloat = 16
    public var radius: CGFloat = 12
}

Applying Styles to Components

Global Application (Recommended)

Apply styles once at app initialization:

import SwiftUI

@main
struct MyApp: App {
    let sdk: DigitalCardEngine
    
    init() {
        // Initialize SDK
        sdk = try! DigitalCardEngine(
            token: "your-token",
            dceConfiguration: DceConfiguration(/* ... */),
            totpCallback: { _ in /* ... */ }
        )
        
        // Apply global theme
        applyCustomTheme()
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(sdk.styleConfiguration)
        }
    }
    
    private func applyCustomTheme() {
        // Customize palette
        sdk.styleConfiguration.palette.secondary = Color(hex: 0x00A86B)
        sdk.styleConfiguration.palette.success = Color(hex: 0x4CAF50)
        
        // Customize spacing
        sdk.styleConfiguration.spacing = 12
        
        // Customize card list
        sdk.styleConfiguration.cardListViewProperties = CardListViewProperties(
            title: true,
            actionButton: true,
            dividers: true,
            cardListLayout: .largeLayout
        )
        
        // Customize card details
        sdk.styleConfiguration.cardDetailsViewProperties = CardDetailsViewProperties(
            actionButton: true,
            slider: false,
            cardInfoLayout: .detailLayout
        )
    }
}

Per-Component Customization

Pass style properties directly to individual components:

struct CustomStyledCardList: View {
    let sdk: DigitalCardEngine
    
    var body: some View {
        try? sdk.getCardListView(
            properties: CardListViewProperties(
                title: false,
                actionButton: true,
                cardListLayout: .defaultLayout
            ),
            style: CardListViewStyle(
                internalHorizontalPadding: 20,
                internalVerticalPadding: 16,
                externalPadding: 24,
                internalGap: 20,
                radius: 16
            ),
            onSelect: { card in /* ... */ },
            onAddCard: { /* ... */ }
        )
    }
}

Environment Object Pattern

Use SwiftUI's environment to propagate styling:

struct ThemedView: View {
    let sdk: DigitalCardEngine
    
    var body: some View {
        NavigationStack {
            ContentView()
        }
        .environmentObject(sdk.styleConfiguration)
    }
}

struct ContentView: View {
    @EnvironmentObject var styleConfig: StyleConfiguration
    
    var body: some View {
        VStack {
            Text("Custom Styled")
                .font(styleConfig.fonts.titleLarge.font)
                .foregroundColor(styleConfig.palette.onSurface)
        }
    }
}

Best Practices

  1. Apply globally: Set styles once at app startup via sdk.styleConfiguration
  2. Use semantic colors: Leverage the Palette system rather than hardcoding colors
  3. Maintain consistency: Keep spacing and typography consistent across screens
  4. Test accessibility: Ensure text contrasts meet WCAG standards
  5. Avoid per-component overrides: Only override when absolutely necessary
  6. Document custom themes: Keep a reference of your color/font choices

Complete Example

import SwiftUI

@main
struct DigitalCardApp: App {
    let sdk: DigitalCardEngine
    
    init() {
        // Initialize SDK
        let config = DceConfiguration(
            clientId: 12345,
            accountId: "account-id",
            userId: "user-id",
            apiKeyId: "api-key-id",
            apiKey: "api-key"
        )
        
        sdk = try! DigitalCardEngine(
            token: "auth-token",
            dceConfiguration: config,
            totpCallback: { cardId in
                return "totp-secret"
            }
        )
        
        // Apply comprehensive theme
        configureTheme()
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView(sdk: sdk)
                .environmentObject(sdk.styleConfiguration)
        }
    }
    
    private func configureTheme() {
        // Colors
        sdk.styleConfiguration.palette = Palette(
            surface: Color.white,
            onSurface: Color(hex: 0x1A1A1A),
            onSurfaceVariant: Color(hex: 0x666666),
            primary: Color(hex: 0x0019BF),
            secondary: Color(hex: 0x00A86B),
            success: Color(hex: 0x4CAF50),
            outlineVariant: Color(hex: 0xE0E0E0),
            surfaceContainerHighest: Color(hex: 0xF5F5F5),
            surfaceContainerLow: Color(hex: 0xF9F9F9)
        )
        
        // Global settings
        sdk.styleConfiguration.spacing = 12
        
        // Card list preferences
        sdk.styleConfiguration.cardListViewProperties = CardListViewProperties(
            title: true,
            actionButton: true,
            dividers: true,
            cardListLayout: .largeLayout
        )
        
        sdk.styleConfiguration.cardListViewStyle = CardListViewStyle(
            internalHorizontalPadding: 16,
            internalVerticalPadding: 12,
            externalPadding: 16,
            internalGap: 16,
            radius: 12
        )
        
        // Card details preferences
        sdk.styleConfiguration.cardDetailsViewProperties = CardDetailsViewProperties(
            actionButton: true,
            slider: false,
            cardInfoLayout: .detailLayout
        )
        
        // Transaction history preferences
        sdk.styleConfiguration.transactionHistoryViewProperties = TransactionHistoryViewProperties(
            leadingElement: .icon,
            groupByMonth: true,
            timestampFormat: "MMM dd, yyyy",
            filter: true,
            numberOfTransactions: 10
        )
    }
}

struct ContentView: View {
    let sdk: DigitalCardEngine
    @EnvironmentObject var styleConfig: StyleConfiguration
    
    var body: some View {
        NavigationStack {
            VStack {
                Text("Digital Card Engine")
                    .font(styleConfig.fonts.titleLarge.font)
                    .foregroundColor(styleConfig.palette.onSurface)
                
                try? sdk.getCardListView(
                    onSelect: { card in
                        // Handle selection
                    },
                    onAddCard: {
                        // Handle add card
                    }
                )
            }
        }
    }
}

Troubleshooting

Styles not applying: Ensure you modify sdk.styleConfiguration after initialization but before rendering UI components.

Colors look wrong: Verify you're using Color(hex:) format with proper hex values.

Fonts not loading: Check that custom font files are included in the app bundle and properly registered in Info.plist.

Environment object not found: Ensure .environmentObject(sdk.styleConfiguration) is applied to the view hierarchy.


See Also