Skip to content

feat: runtime theme switching + second design-token colour theme (selfFridge)#88

Draft
khoinguyen-mindera wants to merge 25 commits into
mainfrom
feat/theme-switching
Draft

feat: runtime theme switching + second design-token colour theme (selfFridge)#88
khoinguyen-mindera wants to merge 25 commits into
mainfrom
feat/theme-switching

Conversation

@khoinguyen-mindera

Copy link
Copy Markdown
Contributor

Ticket

n/a (free-form feature)

Summary

  • Second colour theme "selfFridge" generated from the design tokens alongside Alfie. Its surfaces use the Selfridges brand yellow (neutrals-0/100/200) so the switch is clearly visible for testing; dark neutrals stay dark for text contrast.
  • Design-token generator (Tools/DesignTokenGen): now parses hex colours, loads each .primitives.* theme mode into its own palette, and emits a ColourPalette struct + AppTheme enum + a ThemeColours.current holder. Primitives.Colours.* became computed forwarders to the active palette — so all ~429 existing call sites reskin with zero edits.
  • Persistence + apply: new ThemeService (Model, mirrors ApiEndpointService) persists the selected theme id and soft-reboots via AppDelegate.rebootApp(); AppDelegate.bootstrap applies the palette + UIKit appearance before first render, so the selection survives relaunch.
  • Debug menu → Theme picker: lists Alfie / Selfridges, marks the current one, switches on tap. Wired through ServiceProviderHomeDependencyContainerDebugMenuViewModel; new AccessibilityID entries.
  • Converted 14 static let colour captures (TabControl / ThemedToggle / SizingSwatch) to computed vars so they don't freeze on a soft-reboot.
  • Codegen unit tests added (hex parsing, multi-theme emission, schema-drift + empty-colour guards). ThemeService unit tests added.

Verification

  • ./Alfie/scripts/verify.sh --skip-integration: build + unit PASSED. Integration was skipped (needs the Alfie-BFF repo as a sibling checkout; unavailable in this worktree) — this change does not touch networking.

Notes / follow-ups

  • selfFridge's yellow surfaces are a deliberately bold test palette; can be dialled back to a production-realistic palette on request.
  • Swift 6: ThemeColours.current is a mutable non-isolated global (safe today — main-thread only); annotate @MainActor when SharedUI adopts strict concurrency.

Screenshot

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds runtime theme switching to Alfie iOS by extending the design-token generator to emit multiple colour palettes (one per .primitives theme mode) and introducing a persisted theme selection that is applied during app bootstrap (and on soft-reboot), so the chosen theme survives relaunch.

Changes:

  • Design-token generator now supports multi-theme colour palettes + hex colour parsing, emitting ThemeColours, ColourPalette, and AppTheme while making Primitives.Colours.* forward to the active palette.
  • New ThemeService persists the selected theme id and triggers a soft-reboot; app bootstrap applies the persisted theme before first render / appearance setup.
  • Debug Menu gains a Theme picker UI and related accessibility identifiers; several UI components switch static colour captures to computed vars to avoid “frozen” colours after reboot.

Reviewed changes

Copilot reviewed 33 out of 33 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
Tools/DesignTokenGen/Tests/DesignTokenGenCoreTests/GeneratorTests.swift Adds tests for multi-theme loading, hex parsing, and multi-theme emission/guards.
Tools/DesignTokenGen/Tests/DesignTokenGenCoreTests/Fixtures/mini/manifest.json Extends mini fixture to include a second .primitives theme mode.
Tools/DesignTokenGen/Tests/DesignTokenGenCoreTests/Fixtures/mini/.primitives.selffridge-theme.tokens.json Adds mini fixture token file for the second theme (hex colours).
Tools/DesignTokenGen/Tests/DesignTokenGenCoreTests/CoverageTests.swift Updates expected written files and adjusts colour literal tests to target ThemeColours.
Tools/DesignTokenGen/Sources/DesignTokenGenCore/TokenLoader.swift Loads per-theme colour palettes, adds .primitives theme mode handling, and parses hex colour strings.
Tools/DesignTokenGen/Sources/DesignTokenGenCore/Token.swift Adds TokenValue.isColour helper for filtering colour tokens.
Tools/DesignTokenGen/Sources/DesignTokenGenCore/Emitter.swift Emits new ThemeColours+Generated.swift and changes colours/semantic tokens to computed forwarders.
Alfie/AlfieKit/Tests/ModelTests/ThemeServiceTests.swift Adds unit tests for theme persistence + reboot triggering.
Alfie/AlfieKit/Tests/DebugMenuTests/ThemePickerViewModelTests.swift Adds unit tests for theme picker view model behavior.
Alfie/AlfieKit/Sources/SharedUI/Theme/Toggle/ThemedToggleView.swift Converts captured theme colours to computed vars to reflect runtime theme switches.
Alfie/AlfieKit/Sources/SharedUI/Theme/Components/SizingBanner/SizingSwatchView.swift Converts a captured colour constant to computed to avoid caching across reboot.
Alfie/AlfieKit/Sources/SharedUI/GeneratedTokens/ThemeColours+Generated.swift New generated multi-theme palette holder + AppTheme enum.
Alfie/AlfieKit/Sources/SharedUI/GeneratedTokens/Theme+Generated.swift Changes semantic colour tokens to computed vars so they track active theme.
Alfie/AlfieKit/Sources/SharedUI/GeneratedTokens/Primitives+Generated.swift Changes primitive colours to forward to ThemeColours.current.
Alfie/AlfieKit/Sources/SharedUI/DesignTokens/manifest.json Adds second .primitives theme mode for SharedUI token set.
Alfie/AlfieKit/Sources/SharedUI/DesignTokens/.primitives.selffridge-theme.tokens.json Adds second theme primitive colour token set (hex colours).
Alfie/AlfieKit/Sources/SharedUI/Components/TabControl/TabControl.swift Converts captured colours to computed vars for runtime theme switching.
Alfie/AlfieKit/Sources/Model/Services/Theme/ThemeServiceProtocol.swift Introduces service protocol for persisting theme id and rebooting.
Alfie/AlfieKit/Sources/Model/Services/Theme/ThemeService.swift Implements ThemeService backed by UserDefaults.
Alfie/AlfieKit/Sources/Model/Services/ServiceProviderProtocol.swift Exposes themeService on the service provider protocol.
Alfie/AlfieKit/Sources/Mocks/Core/Services/MockThemeService.swift Adds mock implementation for tests and previews.
Alfie/AlfieKit/Sources/Mocks/Core/Services/MockServiceProvider.swift Wires themeService into mock provider.
Alfie/AlfieKit/Sources/Home/UI/HomeViewModel.swift Passes themeService through to DebugMenu construction.
Alfie/AlfieKit/Sources/Home/Models/HomeDependencyContainer.swift Adds themeService to Home dependency container.
Alfie/AlfieKit/Sources/DebugMenu/UI/Theme/ThemePickerViewModel.swift Implements theme picker logic using AppTheme + ThemeServiceProtocol.
Alfie/AlfieKit/Sources/DebugMenu/UI/Theme/ThemePickerView.swift Adds Theme picker UI list with selection checkmark + accessibility ids.
Alfie/AlfieKit/Sources/DebugMenu/UI/DebugMenuViewModel.swift Adds themeService dependency to DebugMenu view model.
Alfie/AlfieKit/Sources/DebugMenu/UI/DebugMenuView.swift Adds Theme entry to Debug Menu navigation and wires ThemePickerView.
Alfie/AlfieKit/Sources/AppFeature/Navigation/AppFeatureViewModel.swift Injects themeService into Home flow dependencies.
Alfie/AlfieKit/Sources/AccessibilityIdentifiers/AccessibilityID.swift Adds accessibility identifiers for Debug Menu theme picker.
Alfie/AlfieKit/Package.swift Updates module dependencies for new DebugMenu/theme picker and tests.
Alfie/Alfie/Service/ServiceProvider.swift Instantiates and exposes ThemeService in app service provider.
Alfie/Alfie/Delegate/AppDelegate.swift Applies persisted theme during bootstrap before first UI render / appearance setup.

Comment on lines +52 to 54
"selffridge-theme": [
".primitives.selffridge-theme.tokens.json"
]
Comment on lines +7 to +9
private let userDefaults: UserDefaultsProtocol
private let storageKey = "SELECTED_THEME_ID"
private weak var appDelegate: AppDelegateProtocol?
Base automatically changed from ALFMOB-266-integrate-typography-tokens to main July 6, 2026 03:12
@khoinguyen-mindera
khoinguyen-mindera marked this pull request as draft July 7, 2026 06:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants