Extract colors and apply cozy theme - #8
Draft
gsbernstein wants to merge 18 commits into
Draft
Conversation
Centralize theme color definitions in a single AppColors enum and update views to reference named colors instead of inline system colors. No visual changes in this commit. Co-authored-by: Greg <gsbernstein@users.noreply.github.com>
Replace the default system blues and purples with a warm palette inspired by the app icon: coral accents, cream backgrounds, dusty mauve and brown sleep-stage colors, and sage/terracotta status tones. Asset catalog colors now use adaptive light/dark cozy values. Co-authored-by: Greg <gsbernstein@users.noreply.github.com>
Reconcile the cozy color palette with upstream changes that landed since this branch was created (editable wake time/goal, sleep balance waterfall chart, sleep insights card, earliest-bedtime setting, etc.): - Constants.sleepGoalColor now uses AppColors.positive/negative instead of hardcoded .green/.red, so every consumer (LastNightCard, SleepDayGroup, SleepBankCard) picks up the cozy palette automatically. - Card headers use CardHeader with cozy AppColors icon tints instead of the old inline HStack layout and system blue/red/purple colors. - SleepBankCard and LastNightCard keep master's grace-period-aware status color logic (sleepBank.statusColor / durationColor). - SettingsView keeps master's earliest-bedtime DatePicker, dropping the now-removed max/min sleep hour sliders. - Extended the cozy theme to new views added upstream: SleepInsightsCard (green/orange -> AppColors.positive/warning) and BalanceWaterfallChart (green/red -> AppColors.positive/negative). Co-authored-by: Greg <gsbernstein@users.noreply.github.com>
Add an AppTheme enum (system / cozy / cozyBlack) and thread it through the app via EnvironmentValues.appTheme, so every color decision reacts to the selected theme instead of a fixed constant: - AppColors.swift: every color accessor now takes an AppTheme and returns either the original system colors (blue/purple/red/green/orange/indigo/gray, as they were before the cozy palette existed) or the cozy warm palette. Background/card colors move here too (previously baked into the AccentColor/BackgroundBehindCards/CardBackground asset catalog entries), so they can vary by theme: cozy keeps the existing warm dark-brown dark mode, cozyBlack swaps it for a true black background with a near-black card color for OLED/maximum-contrast displays. - UserPreferences gains a persisted theme property (stored as a raw string, defaulting to cozy, falling back to cozy on an unrecognized value). - SettingsView adds an Appearance section with a theme picker. - ContentView injects the selected theme into the environment and applies it to the background and the app-wide tint. - Every view/model that previously read a fixed AppColors constant now takes an AppTheme parameter (via the appTheme environment key, or for SettingsView directly from the model to avoid depending on environment propagation into its sheet/inspector presentation).
Replace the theme-parameterized AppColors functions with a ThemeColorPalette protocol and one conforming struct per theme (SystemThemeColors, CozyThemeColors, CozyBlackThemeColors). AppTheme.colors hands back the palette for the active case, so call sites read colors.accent / theme.colors.background etc. instead of passing the theme into a switch on every access. CozyBlackThemeColors composes CozyThemeColors and only overrides background/cardBackground, so the two palettes can never drift apart on the colors they share. Where a color choice itself varies per case of some other type (sleep stage colors), that type now returns a KeyPath<any ThemeColorPalette, Color> instead of switching on the theme, and the call site resolves it against the active palette (HKCategoryValueSleepAnalysis.color(in:), replacing color(for:)). Verified with a standalone Swift 6 snippet that keypaths rooted in an existential protocol resolve correctly against different conforming struct instances, including under strict concurrency checking. Constants.sleepGoalColor/sleepDurationColor and SleepBank.statusColor now take the resolved palette directly (positive/negative colors, not a keypath, since they also need to fall back to a caller-supplied grace color that isn't part of the palette). SettingsView keeps resolving preferences.theme.colors directly rather than through the appTheme environment key, since it's presented as a sheet/inspector and this avoids depending on environment propagation into that presentation.
Co-authored-by: Greg <gsbernstein@users.noreply.github.com>
Co-authored-by: Greg <gsbernstein@users.noreply.github.com>
Add one colorset per cozy color (Assets.xcassets/Cozy*.colorset) with the same light/dark RGB values that were previously inline in CozyThemeColors.swift, and switch CozyThemeColors to read the generated Color extensions (Color.cozyAccent, Color.cozyBackground, etc.) instead of building UIColor dynamic providers from hardcoded tuples. This only touches the cozy theme: SystemThemeColors intentionally mirrors the OS's own semantic colors (.blue, .secondarySystemBackground, etc.), so there's nothing there to make tunable via an asset. The generated symbols already work today because the project has ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS enabled, and the .xcodeproj uses a file-system-synchronized group, so the new colorsets don't need any project.pbxproj changes to be picked up. Removed the now-unused RGB-tuple dynamicColor(light:dark:) helper from ThemeColorPalette.swift; only the UIColor-pair overload (used by SystemThemeColors) is still needed. Net effect: tweaking the cozy palette (e.g. lightening a sleep-stage color, or adjusting how dark the near-black card is) is now a matter of picking a new color in Xcode's asset catalog editor, with live light/dark preview, rather than editing RGB tuples in code.
…ency with SystemThemeColors Co-authored-by: Greg <gsbernstein@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ThemeColorPaletteprotocol with one conforming struct per theme (SystemThemeColors,CozyThemeColors), rather than a set of functions that switch on the theme.AppTheme.colorshands back the palette for the active case, and call sites readcolors.accentetc., or resolve aKeyPath<any ThemeColorPalette, Color>when the choice itself depends on some other value (e.g. sleep stage colors, sleep-goal status colors).Assets.xcassets/Cozy*.colorset) instead of inline RGB values, so they can be tweaked visually in Xcode's color picker.Changes
Color extraction & cozy theme
ThemeColorPaletteprotocol (Utils/ThemeColorPalette.swift) with named colors for accents, card icons, status indicators, sleep stages, backgrounds, andprimary/secondary(so "grace"/fallback colors can also be expressed as a keypath).SystemThemeColors— the original default iOS colors (blue/purple/red/green/orange/indigo/gray,secondarySystemBackground/systemBackground/tertiarySystemBackground), recovered from before the cozy palette existed. Intentionally mirrors the OS's own semantic colors rather than the asset catalog, since there's nothing there to make tunable.CozyThemeColors— reads every color from a generatedColor.cozy*extension backed by an asset-catalog colorset (CozyAccent,CozyBackground,CozySleepDeep, etc.), each with its own light/dark variant. Dark mode uses a true-black background with a near-black card color..blue,.purple,.red, etc.Theme as a setting
AppThemeenum (system,cozy) threaded through the view hierarchy viaEnvironmentValues.appTheme, with acolors: any ThemeColorPalettecomputed property.KeyPath<any ThemeColorPalette, Color>(ornilfor a fallback) instead of switching on the theme itself — e.g.HKCategoryValueSleepAnalysis.paletteColor,Constants.sleepGoalColor(difference:),SleepBank.statusColor. The call site resolves the keypath against the active palette (colors[keyPath:]).UserPreferencesgains a persistedthemeproperty (stored as a raw string, defaulting tocozy, falling back tocozyon an unrecognized value so future app versions can add themes without breaking older data).SettingsViewadds an "Appearance" section with a theme picker; it resolvespreferences.theme.colorsdirectly rather than through theappThemeenvironment key, since it's presented as a sheet/inspector.ContentViewinjects the selected theme into the environment and applies it to the background and the app-wide tint, so default-tinted system controls (buttons, toggles, nav bar) follow the theme too.Test plan
Assets.xcassetsin Xcode and confirm the newCozy*colorsets show up correctly and are editable/previewable