fix(theme): light-mode color parity with Wisp Android#162
Merged
Conversation
Mirrors Wisp Android PR barrydeen/wisp#558 on the iOS side. Two coupled fixes for light mode that were behaving differently from the Android counterpart: 1. **Default-theme light primary deepened to `#D9730D`** (was `#CC7000`). Better contrast against the near-white surface so tinted buttons / icons keep their interactive read. Zap + bookmark surfaces on the default theme now use the same value — previously they sat at `#B85C00`, one shade darker than the primary, producing a visible two-tone mismatch on every zap icon, zap count, top-zapper indicator, etc. 2. **In-flight bolt animation gets its own color**. Plain `wispZapColor` reads muddy when used for the celebratory pulse on a light surface (because the primary is darkened for button contrast); the burst needs a brighter floor. Added `wispZapAnimationColor`, derived from `wispZapColor` via saturation × 1.15 (capped at 1.0) and brightness ≥ 0.5. `LightningPulseView` is the only consumer — every other zap surface stays on plain `wispZapColor`. `ResolvedTheme` gains resolved `zap` / `bookmark` / `zapAnimation` fields so a user-picked accent flows through to every zap surface for the `custom` theme (zap == primary in custom). Other presets (Nord, Dracula, etc.) keep their curated palette values unchanged. Custom-accent light primary derivation: default Spark-orange accent uses the curated `#D9730D` light primary directly; any other accent gets `darkenColor(accent, 0.18)` (HSL lightness × 0.82). Replaces the prior `dimmedForLight` which capped brightness/saturation asymmetrically. See LIGHT_MODE_COLOR_PARITY.md in the Wisp Android repo for the locked cross-platform contract.
The `SparkBreezLogo` SVG is rendered with white paths (a hand-off from the dark-only origin of the wallet dashboard). In light mode that left the logo invisible against the near-white surface in the wallet dashboard header and the settings Spark-details row. Flipped the asset's `template-rendering-intent` from `original` to `template` so the foregroundStyle modifier tints it. Use cases: - Wallet dashboard top bar + Settings Spark-details row → `wispOnSurface` so the logo auto-adapts (near-black on light, light grey on dark) the same way every other text label in the wallet UI does. - Settings "Powered by Breez SDK" footer → `.tertiary` so the logo's prominence matches the caption next to it. The prior `.saturation(0)` modifier was a no-op (the SVG was already monochrome white) so it's dropped. Mirrors the visual-readability spirit of the Wisp Android PR barrydeen/wisp#558 launcher-icon swap — both surfaces needed a light-mode appearance their dark-only assets weren't providing.
Replaces the prior `Canvas` + `boltPath` implementation with a true SwiftUI `Shape` (`BoltGlyph`) consumed via `.stroke(lineWidth:)` for the outer halo and `.fill` for the body. Same visual contract as Wisp Android's `LightningAnimation`: filled silhouette + outer stroke + animated pulse + scale breath. Why a `Shape` instead of an `Image`: - SF-Symbol bolts (`bolt.fill` / `bolt`) are SwiftUI `Image`s, not vector paths we can stroke. The only ways to "add an outer stroke" to an `Image` are stacking offset copies (produces aliased bitmap halos visible at the edges, the user-reported "not clean" rendering) or `.shadow(radius:)` (produces a blurred drop shadow, the original light-mode "muddy smear"). - A SwiftUI `Shape` strokes its path mathematically. Half the stroke width sits inside the silhouette (covered by the fill drawn on top) and half outside (the visible outer halo). The result is crisp at every scale and free of aliasing artifacts. Bolt path is hand-built from a normalized 0-1 coordinate space (see `BoltGlyph`), approximating Wisp Android's `icBoltPath` viewBox 55×94. The animation always renders the bolt regardless of the user's chosen zap-icon style — the in-flight pulse is iconic, not skinnable. Static icon elsewhere still honors `AppSettings.zapImage`. `image:` parameter is kept with a default value for source-compat with both the bare `LightningPulseView()` callsite on main and the `LightningPulseView(image: settings.zapImage)` form introduced on `feat/one-tap-zap`. Pairs with the other two commits on this branch: `wispZapAnimationColor` (the vivid color this animation consumes) and the SparkBreez logo light-mode tinting (the other piece of light-mode color parity).
Three light-mode fixes on the login surfaces:
1. `LoginView`'s "Log In" header was hardcoded `.foregroundStyle(.white)`
so it rendered as white-on-light-grey in light mode (invisible
against the sheet's `wispBackground`). Switched to
`Color.wispOnSurface` which adapts: near-black on light surfaces,
light grey on dark.
2. Same bug on `SplashView`'s `NostrLoginSheet` title
("Continue with Nostr"). Same fix.
3. `WispLogo` SVG uses `fill-rule="evenodd"` so the wisp's two eye
sub-paths render as cut-outs — they reveal whatever's behind the
logo. On light mode that's the `wispBackground` light grey, so the
eyes effectively vanished. Added two explicit `fill="#000000"`
paths for the eyes drawn before the wisp body. The body's
cut-outs now reveal the black layer beneath instead of the
surface color, locking the eye color regardless of the theme.
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
iOS counterpart to Wisp Android PR barrydeen/wisp#558. Light mode has been drifting from the Android counterpart in three places — colors, the wallet logo's visibility, and the in-flight bolt pulse animation. This PR brings all three back into parity.
1. Light-mode primary deepened + zap surfaces aligned
primarybumped from#CC7000to#D9730D. Better contrast on the near-white surface so tinted buttons / icons keep their interactive read.zap+bookmarkbumped from#B85C00to the same#D9730D. They were one shade darker than the primary, producing a visible two-tone mismatch on every zap icon, count, and top-zapper indicator.ResolvedThemegains resolvedzap/bookmark/zapAnimationfields. For thecustomtheme these track the resolvedprimaryso a user-picked accent flows through to every zap surface as one hue. For every other preset (Nord, Dracula, etc.) they pass through the curated palette values unchanged.#D9730Ddirectly; any other accent getsdarkenColor(accent, 0.18)(HSL lightness × 0.82). Replaces the priordimmedForLightwhich capped brightness/saturation asymmetrically.2. In-flight bolt pulse: vector stroke instead of shadow blur
Color.wispZapAnimationColoraccessor, derived fromwispZapColorvia saturation × 1.15 (capped at 1.0) and brightness ≥ 0.5. PlainwispZapColorreads muddy in light mode because the primary is darkened for button contrast; the celebratory pulse needs a brighter floor.LightningPulseViewrewritten: instead of stacked.shadow(radius:)drop shadows (blurred, smeared on near-white surfaces), the bolt is now drawn as a true SwiftUIShape(BoltGlyph) with.stroke(lineWidth:)for the outer halo and.fillfor the body. The stroke is mathematically half-inside / half-outside the silhouette — the fill covers the inside half so only the outer halo is visible. Crisp at every size, no aliasing.LightningAnimation: same bolt geometry, same stroke width / fill / pulse composition, same scale-breath envelope (0.92 → 1.08).3. SparkBreez wallet logo readable in light mode
SparkBreezLogoSVG is rendered with all-white paths (legacy of the dark-only wallet origin). In light mode that left the logo invisible against the near-white surface in both the wallet dashboard header and the settings Spark-details row.template-rendering-intentfromoriginaltotemplateso aforegroundStylemodifier tints it. Three sites:wispOnSurface(near-black on light, light grey on dark)..tertiaryso the logo's prominence matches the caption next to it.See
LIGHT_MODE_COLOR_PARITY.mdin the Wisp Android repo for the locked cross-platform contract.Test plan
#D9730D, same orange as Save / Connect / Confirm buttons.#D9730D.#D9730D. SparkBreez logo in the top bar is clearly readable (near-black, not invisible white).primary/zap/bookmarkvalues stay intact, unaffected by this PR.