Skip to content

AccentColor is configured correctly but not honoured — system controls fall back to blue #102

Closed

Description

@Adron

Found while implementing #80 (see PR #101).

What was observed

  • ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor is set in the build settings.
  • Assets.xcassets/AccentColor.colorset/Contents.json does hold the brand green, with a correct
    dark-appearance variant.
  • Yet links and system controls render system blue, not brand green.

Setting .tint(ILColor.primary) on RootView fixes it for the hierarchy below that point, and #101
does exactly that. But that is a workaround layered over an unexplained cause, not a fix.

Why it matters beyond links

.tint does not cross into sheet content — proven while answering #80's deep-link question: a
sheet presented from InterlinedListApp inherited the colour scheme but kept a system-blue "Cancel"
until its content was tinted directly.

So every independently-presented surface needs its own .tint, and any one that is missed silently
reverts to blue. A correctly-honoured global accent would cover all of them at once. Today the app
has an unknown number of un-tinted sheets.

Worth investigating

  • Why the global accent is ignored. Candidates: the colorset name not matching what the build
    setting expects, the asset not being compiled into the right catalog, or Info.plist /
    UIWindow level configuration overriding it.
  • Whether UIView.appearance().tintColor (set alongside the existing
    UINavigationBar.appearance() configuration in InterlinedListApp.configureNavigationBarAppearance())
    would cover UIKit-hosted surfaces the SwiftUI .tint cannot reach.
  • Audit every .sheet / .fullScreenCover in Views/ for a missing tint once the root cause is
    known — if the global accent can be made to work, they can all drop their local tints instead.

Acceptance criteria

  • The cause is identified and written down (even if the conclusion is "the global accent cannot
    work here, tint explicitly").
  • No surface renders system blue where brand green is intended, in both appearances.
  • If explicit tinting stays the answer, there is one documented place to add it for a new sheet.

Low urgency — #101 makes the visible case correct. This is about not having an unexplained
workaround and an unaudited set of sheets.

Activity

  1. added
    bugSomething isn't working
    ws:papercutsW8 — small parity papercuts
    on Sep 16, 2026
  2. Adron commented on Sep 17, 2026

    @Adron
    MemberAuthor

    Fixed in #113, now merged. Closing.

    Root cause — not what this issue guessed

    The asset, the build setting and Info.plist were all correct. Verified against the built
    product
    rather than the source: InterlinedList.app/Info.plist carries NSAccentColorName = AccentColor, assetutil --info Assets.car shows the colour compiled in with both appearance
    variants, and UIColor(named: "AccentColor") resolves to brand green at runtime.

    The app was destroying its own accent at launch. configureNavigationBarAppearance() ran from
    InterlinedListApp.init(), and these two lines are the trigger:

    let back = UIBarButtonItemAppearance()
    back.normal.titleTextAttributes = [.foregroundColor: UIColor.white]

    Materialising a bar-button state appearance that early resolves UIKit's default bar-button
    styling before UIApplication has adopted the accent asset. UIKit then caches system blue as the
    process tint for the rest of the launch, so UIColor.tintColor and Color.accentColor stay blue
    permanently.

    Narrowed statement by statement: constructing UIBarButtonItemAppearance() without touching
    .normal, setting UINavigationBar.appearance().tintColor, assigning a UINavigationBarAppearance
    to the proxy, and reading UINavigationBar.appearance() are each harmless alone. A differential
    control app with a red accent honoured it on the same Xcode/simulator — and still did after being
    handed this app's Info.plist verbatim — so neither the custom plist nor the iOS version was ever
    implicated.

    UIView.appearance().tintColor (candidate #2 in this issue) was not needed; the real accent
    covers everything it would have.

    The fix

    configureNavigationBarAppearance() moved to AppDelegate.application(_:didFinishLaunchingWithOptions:).
    Both hierarchy-wide .tint workarounds are gone. AppAccentColorTests guards it — the two
    process-tint assertions genuinely fail when the old init() is restored.

    Sheet audit

    64 .sheet presentations across 22 files; zero .fullScreenCover. Exactly one carried a tint
    (the deep-link sheet); every other relied on RootView's tint reaching it — which worked for sheets
    below RootView and not for the one presented above it. With a working accent, a new sheet now
    needs nothing
    . Remaining .tint call sites are legitimately local (swipe-action backgrounds, a
    ProgressView on a dark overlay) and were left alone. The convention is documented in CLAUDE.md.

    Bonus bug fixed

    The deep-link sheet's explicit .tint(ILColor.link) was also a contrast bug: ILColor.link is
    #184860, identical to the masthead, so that sheet's "Cancel" was invisible. Screenshot-confirmed.

    Deliberate colour decision

    Links move from teal #184860 to green #2FA877 — app-wide, since the accent reaches everything.
    Screenshots of both were compared side by side and green was chosen deliberately: it is what the
    asset holds, and green is already the action colour in 30+ places versus 3 for link.

    Not verified

    No authenticated surface was seen rendered — feed, lists, documents, settings and the ~60 sheets they
    present. The mechanism is process-wide and test-asserted, so they should follow, but that is
    inference. Only the login screen and the deep-link sheet were observed directly, in both appearances.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingws:papercutsW8 — small parity papercuts

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions