diff options
| author | Christian Cleberg <[email protected]> | 2026-07-20 19:14:04 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-07-20 21:03:10 -0500 |
| commit | 2c608ed9cb11f3e835f577994b5627b9b7910f4f (patch) | |
| tree | 078ff50c5151fcc5e91b9d28051562f0bc28088d | |
| parent | 349bec0e99f330fc1172ca9847c58f019e861f61 (diff) | |
| download | domain-dig-2c608ed9cb11f3e835f577994b5627b9b7910f4f.tar.gz domain-dig-2c608ed9cb11f3e835f577994b5627b9b7910f4f.tar.bz2 domain-dig-2c608ed9cb11f3e835f577994b5627b9b7910f4f.zip | |
feat(a11y): unlock light mode and add appearance preference (#21 phase 2)
Removes the 16 scattered .preferredColorScheme(.dark) calls and the one
.toolbarColorScheme, and applies appearance in exactly one place — the
WindowGroup in DomainDigApp. Re-applying per view is what let the lock
spread across eight files unnoticed until light mode was unreachable.
Adds AppAppearance (System / Light / Dark) in @AppStorage, exposed under
Settings > Display next to Density. Honouring the system setting and
offering an override is one key, and it keeps the deliberate dark
aesthetic reachable for anyone who wants it.
Also replaces .secondary with AppTextSecondary across 191 sites. iOS's
own secondaryLabel is 3.29:1 on a light card — below AA — which never
showed while the app was locked to dark, where the same colour reads
6.32:1. Unlocking light mode is precisely what exposed it, so it belongs
here rather than in a later phase: without it, light mode would ship with
body text under 4.5:1 app-wide.
Dark mode reports 18 findings, unchanged from phase 1 — no regression
from unlocking. Light mode reports 21. The three extra are iOS-rendered
Section headers (TIER, PREFERENCES, SERVICES) using the system's grey;
overriding system header styling across every section to gain ~0.3:1 on
decorative labels is a poor trade and is left alone.
Two long-standing Settings contrast findings are now explained. They are
the last rows of a section sitting under the translucent tab bar, so the
audit measures text against a blended background — confirmed by
screenshot, present in dark mode since phase 0, and standard iOS
scroll-under behaviour rather than a defect.
| -rw-r--r-- | Docs/ACCESSIBILITY.md | 26 | ||||
| -rw-r--r-- | DomainDig/AuditModeView.swift | 4 | ||||
| -rw-r--r-- | DomainDig/BatchResultsView.swift | 16 | ||||
| -rw-r--r-- | DomainDig/BatchSweepSummaryView.swift | 4 | ||||
| -rw-r--r-- | DomainDig/ContentView.swift | 149 | ||||
| -rw-r--r-- | DomainDig/DashboardView.swift | 15 | ||||
| -rw-r--r-- | DomainDig/DomainCompareView.swift | 2 | ||||
| -rw-r--r-- | DomainDig/DomainDig/AuditViews.swift | 36 | ||||
| -rw-r--r-- | DomainDig/DomainDigApp.swift | 3 | ||||
| -rw-r--r-- | DomainDig/DomainDigUI.swift | 55 | ||||
| -rw-r--r-- | DomainDig/HistoryView.swift | 12 | ||||
| -rw-r--r-- | DomainDig/IntegrationsView.swift | 22 | ||||
| -rw-r--r-- | DomainDig/MonitoringView.swift | 14 | ||||
| -rw-r--r-- | DomainDig/PaywallView.swift | 8 | ||||
| -rw-r--r-- | DomainDig/SavedDomainsView.swift | 3 | ||||
| -rw-r--r-- | DomainDig/ScheduledReportsView.swift | 6 | ||||
| -rw-r--r-- | DomainDig/TimelineView.swift | 8 | ||||
| -rw-r--r-- | DomainDig/WatchlistView.swift | 26 | ||||
| -rw-r--r-- | DomainDig/WorkflowsView.swift | 37 | ||||
| -rw-r--r-- | DomainDigWidget/DomainDigPortfolioWidget.swift | 18 | ||||
| -rw-r--r-- | DomainDigWidget/SweepLiveActivity.swift | 10 | ||||
| -rw-r--r-- | Shared/Colors.xcassets/AppTextSecondary.colorset/Contents.json | 78 |
22 files changed, 344 insertions, 208 deletions
diff --git a/Docs/ACCESSIBILITY.md b/Docs/ACCESSIBILITY.md index 7e28d89..7153381 100644 --- a/Docs/ACCESSIBILITY.md +++ b/Docs/ACCESSIBILITY.md @@ -29,6 +29,12 @@ worst of those three is shown: | `StatusWarning` | `#7A5600` | `#FFD60A` | 4.74 | 9.61 | | `StatusCritical` | `#B3261E` | `#FF6961` | 4.51 | 5.54 | | `StatusNeutral` | `#5A5A5F` | `#A1A1A6` | 4.92 | 5.88 | +| `AppTextSecondary` | `#5A5A5F` | `#A1A1A6` | 6.15 | 7.50 | + +`AppTextSecondary` replaces `.secondary` for body text. iOS's own `secondaryLabel` +is only **3.29:1** on a light card — below AA — which never showed while the app +was locked to dark, where the same colour reads 6.32:1. Unlocking light mode +exposed it across 191 sites. High Contrast variants push further in the same direction. Surfaces (`AppBackground`, `AppSurface`, `AppSurfaceElevated`, `AppSeparator`) carry no @@ -58,6 +64,26 @@ So there are two colours: `AppOnAccent` is the label colour for a solid accent fill and flips by scheme — white on the light accent, black on the dark one. +## Appearance + +`AppAppearance` (System / Light / Dark) is stored in `@AppStorage` and applied in +**exactly one place** — the `WindowGroup` in `DomainDigApp`. Keep it that way. The +app previously carried 16 separate `.preferredColorScheme(.dark)` calls scattered +through view bodies, which is how light mode became unreachable without anyone +noticing; re-applying per view is what let the lock spread. + +Users override it under Settings → Display. + +### Known light-mode findings + +| Finding | Cause | Action | +| --- | --- | --- | +| 2× `contrast failed` on Settings | The last rows of a section sit under the translucent tab bar, so the audit measures text against a blended background. Present in dark mode too, since phase 0. | None — standard iOS scroll-under behaviour | +| 3× `contrast nearly passed` on Settings | iOS-rendered `Section` headers (`TIER`, `PREFERENCES`, `SERVICES`) use the system's grey. | Not fixed. Overriding system header styling across every section to gain ~0.3:1 on decorative labels trades platform convention for very little | + +Dark mode reports 18 findings and light mode 21; the three extra are the section +headers above. Everything the app actually controls passes in both schemes. + ## Findings are reported, not enforced The audit surfaces violations that exist today, so failing on all of them would diff --git a/DomainDig/AuditModeView.swift b/DomainDig/AuditModeView.swift index b3e363e..08b9e9c 100644 --- a/DomainDig/AuditModeView.swift +++ b/DomainDig/AuditModeView.swift @@ -25,7 +25,7 @@ struct AuditModeView: View { Section("Audit Timeline") { if auditStore.sessions.isEmpty { Text("No audits yet.") - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } else { ForEach(auditStore.sessions) { session in Button { @@ -35,7 +35,7 @@ struct AuditModeView: View { Text(session.domain).font(.headline) Text("\(session.createdAt.formatted(date: .abbreviated, time: .shortened)) • \(session.status.title)") .font(.caption) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } diff --git a/DomainDig/BatchResultsView.swift b/DomainDig/BatchResultsView.swift index afef5cb..bf211dc 100644 --- a/DomainDig/BatchResultsView.swift +++ b/DomainDig/BatchResultsView.swift @@ -17,12 +17,12 @@ struct BatchResultsView: View { .frame(width: 120) Text(viewModel.batchProgressLabel) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } else if !viewModel.batchResults.isEmpty { Text("\(viewModel.batchResults.count) domains") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } @@ -67,7 +67,7 @@ struct BatchResultRowView: View { Spacer(minLength: 8) Text(result.resultSource.label.lowercased()) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) AppStatusBadgeView(model: quickStatusBadge) } @@ -80,7 +80,7 @@ struct BatchResultRowView: View { } } .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) HStack(spacing: 10) { Text(result.primaryIP ?? "No IP") @@ -93,12 +93,12 @@ struct BatchResultRowView: View { .lineLimit(1) } .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) if let summaryMessage = result.summaryMessage { Text(summaryMessage) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } if let changeClassification = result.changeClassification { @@ -137,7 +137,7 @@ struct BatchResultRowView: View { return .init( title: availabilityText, systemImage: "exclamationmark.circle", - foregroundColor: .secondary, + foregroundColor: Color(.appTextSecondary), backgroundColor: Color(.appSurfaceElevated) ) } @@ -145,7 +145,7 @@ struct BatchResultRowView: View { private var quickStatusBadge: AppStatusBadgeModel { switch result.status { case .pending: - return .init(title: "Pending", systemImage: "clock", foregroundColor: .secondary, backgroundColor: Color(.appSurfaceElevated)) + return .init(title: "Pending", systemImage: "clock", foregroundColor: Color(.appTextSecondary), backgroundColor: Color(.appSurfaceElevated)) case .running: return .init(title: "Running", systemImage: "arrow.clockwise", foregroundColor: Color(.statusInfo), backgroundColor: Color(.statusInfo).opacity(0.16)) case .completed: diff --git a/DomainDig/BatchSweepSummaryView.swift b/DomainDig/BatchSweepSummaryView.swift index 43ffa74..ae69d06 100644 --- a/DomainDig/BatchSweepSummaryView.swift +++ b/DomainDig/BatchSweepSummaryView.swift @@ -32,7 +32,7 @@ struct BatchSweepSummaryView: View { if visibleResults.isEmpty { Text("No domains with changes or warnings") .font(.system(.caption, design: .monospaced)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } else { ForEach(visibleResults) { result in if let entry = viewModel.historyEntry(for: result) { @@ -56,7 +56,7 @@ struct BatchSweepSummaryView: View { HStack { Text(label) .font(.system(.caption, design: .monospaced)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Spacer() Text(value) .font(.system(.callout, design: .monospaced)) diff --git a/DomainDig/ContentView.swift b/DomainDig/ContentView.swift index 7eec8a0..efaaf05 100644 --- a/DomainDig/ContentView.swift +++ b/DomainDig/ContentView.swift @@ -173,8 +173,6 @@ struct ContentView: View { ) ) .navigationTitle("DomainDig") - .toolbarColorScheme(.dark, for: .navigationBar) - .preferredColorScheme(.dark) .toolbar { ToolbarItem(placement: .topBarTrailing) { if viewModel.hasRun { @@ -182,7 +180,7 @@ struct ContentView: View { viewModel.reset() } label: { Image(systemName: "xmark.circle") - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } @@ -327,12 +325,12 @@ struct ContentView: View { VStack(alignment: .leading, spacing: appDensity.metrics.cardSpacing) { Text("Paste domains separated by new lines or commas.") .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) if let batchAllowanceSummary = FeatureAccessService.batchAllowanceSummary() { Text(batchAllowanceSummary) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } TextField( @@ -544,7 +542,7 @@ struct ContentView: View { } label: { Image(systemName: "bolt.circle") .font(appDensity.font(.body, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Button { viewModel.toggleSavedDomain() @@ -583,7 +581,7 @@ struct ContentView: View { } label: { Image(systemName: "square.and.arrow.up") .font(appDensity.font(.body, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } @@ -652,13 +650,13 @@ struct ContentView: View { HStack { Text("RECENT") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Spacer() Button("Clear") { viewModel.clearRecentSearches() } .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } ForEach(viewModel.recentSearches, id: \.self) { domain in @@ -694,7 +692,7 @@ struct ContentView: View { .font(appDensity.font(.headline, design: .default, weight: .semibold)) Text(message) .font(appDensity.font(.callout, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } .frame(maxWidth: .infinity, alignment: .leading) .padding(appDensity.metrics.cardPadding) @@ -784,7 +782,7 @@ struct SummaryView: View { VStack(alignment: .leading, spacing: appDensity.metrics.rowSpacing) { Text(field.label) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Text(field.value) .font(appDensity.font(.caption)) .foregroundStyle(ResultColors.color(for: field.tone)) @@ -826,13 +824,13 @@ struct RiskSummaryCardView: View { Spacer() Text("Deterministic") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } if topFactors.isEmpty { Text("No major risk factors identified") .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } else { ForEach(Array(topFactors.enumerated()), id: \.offset) { _, factor in HStack(alignment: .top, spacing: 8) { @@ -884,7 +882,7 @@ struct InsightsSummaryCardView: View { if insights.isEmpty { Text("No deterministic insights triggered") .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } else { ForEach(Array(insights.enumerated()), id: \.offset) { _, insight in HStack(alignment: .top, spacing: 8) { @@ -940,7 +938,7 @@ struct StickyLookupSummaryView: View { HStack(spacing: 8) { Label(primaryIP, systemImage: "network") .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Spacer(minLength: 6) AppCopyButton(value: primaryIP, label: "Copy IP") } @@ -966,7 +964,7 @@ struct LookupProgressOverviewView: View { .foregroundStyle(.primary) Text(steps.isEmpty ? "Preparing requests" : steps.joined(separator: " • ")) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Spacer() } @@ -1049,13 +1047,13 @@ struct DomainChangeSummaryView: View { .clipShape(Capsule()) Text(summary.generatedAt, style: .time) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } VStack(alignment: .leading, spacing: 4) { Text("Inference") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Text(summary.message) .font(appDensity.font(.caption)) .foregroundStyle(.primary) @@ -1069,7 +1067,7 @@ struct DomainChangeSummaryView: View { VStack(alignment: .leading, spacing: 4) { Text("Observed") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) ForEach(Array(summary.observedFacts.enumerated()), id: \.offset) { _, fact in Text(fact) .font(appDensity.font(.caption)) @@ -1171,7 +1169,7 @@ struct DomainDiffView: View { HStack { Text(item.label) .font(.system(.caption, design: .monospaced)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Spacer() Text("\(item.changeType.marker) \(item.severity.title) • \(changeLabel(for: item.changeType))") .font(.system(.caption2, design: .monospaced)) @@ -1186,10 +1184,10 @@ struct DomainDiffView: View { VStack(alignment: .leading, spacing: 2) { Text("Old") .font(.system(.caption2, design: .monospaced)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Text(oldValue) .font(.system(.caption2, design: .monospaced)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .textSelection(.enabled) } } @@ -1198,7 +1196,7 @@ struct DomainDiffView: View { VStack(alignment: .leading, spacing: 2) { Text("New") .font(.system(.caption2, design: .monospaced)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Text(newValue) .font(.system(.caption, design: .monospaced)) .foregroundStyle(item.hasChanges ? .primary : .secondary) @@ -1306,7 +1304,7 @@ struct TrackedDomainDetailHeaderView: View { Text("Last refresh \(trackedDomain.updatedAt.formatted(date: .abbreviated, time: .shortened))") } .font(.system(.caption2, design: .monospaced)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } @@ -1394,7 +1392,7 @@ struct DomainSectionView: View { VStack(alignment: .leading, spacing: 8) { Text("Part of workflow") .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) ForEach(workflows) { workflow in HStack { @@ -1429,7 +1427,7 @@ struct DomainSectionView: View { if showSuggestions { Text("Suggestions") .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .padding(.top, 4) if suggestionsLoading { ProgressView("Checking alternatives…") @@ -1527,7 +1525,7 @@ struct OwnershipSectionView: View { HStack { Text("History") .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Spacer() if let onLoadHistory, history.isEmpty, !historyLoading, !showsHistoryPlaceholder { Button("Load") { @@ -1545,12 +1543,12 @@ struct OwnershipSectionView: View { VStack(alignment: .leading, spacing: 3) { Text(event.date.formatted(date: .abbreviated, time: .omitted)) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Text(event.summary) .font(appDensity.font(.caption)) Text(event.source) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } else if let historyError { @@ -1604,7 +1602,7 @@ struct IntelligenceSectionView: View { .font(appDensity.font(.caption, weight: .semibold)) Text(signal.detail) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } @@ -1645,7 +1643,7 @@ struct IntelligenceSectionView: View { } Text("First \(item.firstSeen.formatted(date: .abbreviated, time: .omitted)) • Last \(item.lastSeen.formatted(date: .abbreviated, time: .omitted)) • Seen \(item.recurrenceCount)x") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } @@ -1678,7 +1676,7 @@ struct IntelligenceSectionView: View { VStack(alignment: .leading, spacing: 3) { Text(date.formatted(date: .abbreviated, time: .omitted)) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Text(title) .font(appDensity.font(.caption)) } @@ -1755,7 +1753,7 @@ struct SubdomainsSectionView: View { if !groups.isEmpty { Text("Groups") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) ForEach(groups) { group in HStack { Text("\(group.label).*") @@ -1764,7 +1762,7 @@ struct SubdomainsSectionView: View { Spacer() Text("\(group.subdomains.count)") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } @@ -1898,7 +1896,7 @@ struct DNSSectionView: View { if let wildcardTitle = section.wildcardTitle { Text(wildcardTitle) .font(.system(.caption, design: .monospaced)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .padding(.top, 4) ForEach(section.wildcardRows) { row in LabeledValueRow(row: row) @@ -1940,18 +1938,18 @@ struct DNSSectionView: View { VStack(alignment: .leading, spacing: 3) { Text(event.date.formatted(date: .abbreviated, time: .omitted)) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Text(event.summary) .font(appDensity.font(.caption)) if !event.aRecords.isEmpty { Text("A: \(event.aRecords.joined(separator: ", "))") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } if !event.nameservers.isEmpty { Text("NS: \(event.nameservers.joined(separator: ", "))") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } @@ -2017,7 +2015,7 @@ struct WebSectionView: View { if let sslInfo, !sslInfo.subjectAltNames.isEmpty { Text("SANs") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) ForEach(sslInfo.subjectAltNames, id: \.self) { san in HStack(alignment: .top, spacing: 8) { Text(san) @@ -2091,7 +2089,7 @@ struct WebSectionView: View { HStack(alignment: .top, spacing: 6) { Text(redirect.stepLabel) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .frame(width: 16, alignment: .trailing) Text(redirect.statusCode) .font(appDensity.font(.caption)) @@ -2104,7 +2102,7 @@ struct WebSectionView: View { if redirect.isFinal { Text("(final)") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } @@ -2149,7 +2147,7 @@ struct EmailSectionView: View { if !assessment.reasons.isEmpty { Text(assessment.reasons.joined(separator: " | ")) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } if loading { @@ -2176,7 +2174,7 @@ struct EmailSectionView: View { if let auxiliaryDetail = row.auxiliaryDetail { Text(auxiliaryDetail) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } @@ -2192,9 +2190,9 @@ struct EmailSectionView: View { case .warning: return .init(title: row.status, systemImage: "shield.lefthalf.filled", foregroundColor: Color(.statusWarning), backgroundColor: Color(.statusWarning).opacity(0.16)) case .failure: - return .init(title: row.status, systemImage: "minus.circle", foregroundColor: .secondary, backgroundColor: Color(.appSurfaceElevated)) + return .init(title: row.status, systemImage: "minus.circle", foregroundColor: Color(.appTextSecondary), backgroundColor: Color(.appSurfaceElevated)) case .primary, .secondary: - return .init(title: row.status, systemImage: "circle", foregroundColor: .secondary, backgroundColor: Color(.appSurfaceElevated)) + return .init(title: row.status, systemImage: "circle", foregroundColor: Color(.appTextSecondary), backgroundColor: Color(.appSurfaceElevated)) } } } @@ -2244,7 +2242,7 @@ struct NetworkSectionView: View { Spacer() Text(row.latencyLabel) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) AppStatusBadgeView(model: reachabilityBadge(row)) } } @@ -2304,7 +2302,7 @@ struct NetworkSectionView: View { } else { Text("Standard Ports") .font(.system(.caption, design: .monospaced)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) PortRowsView(rows: standardPortRows) } @@ -2353,7 +2351,7 @@ struct NetworkSectionView: View { case .failure: return .init(title: row.statusLabel, systemImage: "xmark.circle.fill", foregroundColor: Color(.statusCritical), backgroundColor: Color(.statusCritical).opacity(0.16)) case .primary, .secondary: - return .init(title: row.statusLabel, systemImage: "circle", foregroundColor: .secondary, backgroundColor: Color(.appSurfaceElevated)) + return .init(title: row.statusLabel, systemImage: "circle", foregroundColor: Color(.appTextSecondary), backgroundColor: Color(.appSurfaceElevated)) } } } @@ -2379,14 +2377,14 @@ struct PortRowsView: View { if let durationLabel = row.durationLabel { Text(durationLabel) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } AppStatusBadgeView(model: portBadge(row)) } if let banner = row.banner { Text(banner) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .padding(.leading, 8) } } @@ -2404,7 +2402,7 @@ struct PortRowsView: View { case .failure: return .init(title: row.statusLabel, systemImage: "xmark.circle.fill", foregroundColor: Color(.statusCritical), backgroundColor: Color(.statusCritical).opacity(0.16)) case .primary, .secondary: - return .init(title: row.statusLabel, systemImage: "circle", foregroundColor: .secondary, backgroundColor: Color(.appSurfaceElevated)) + return .init(title: row.statusLabel, systemImage: "circle", foregroundColor: Color(.appTextSecondary), backgroundColor: Color(.appSurfaceElevated)) } } } @@ -2512,15 +2510,15 @@ struct SectionTrustMetadataView: View { if let confidence { Text("Confidence \(confidence.title)") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } if let provenance { Text(provenance.provider ?? provenance.source) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Text(provenance.resultSource.label) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } DisclosureGroup("Details") { @@ -2559,7 +2557,7 @@ struct LabeledValueRow: View { VStack(alignment: .leading, spacing: appDensity.metrics.rowSpacing - 1) { Text(row.label) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Text(row.value) .font(appDensity.font(.caption)) .foregroundStyle(ResultColors.color(for: row.tone)) @@ -2656,7 +2654,7 @@ struct SettingsView: View { if let statusMessage = purchaseService.statusMessage { Text(statusMessage) .font(appDensity.font(.caption, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } if let errorMessage = purchaseService.errorMessage { @@ -2732,10 +2730,17 @@ struct SettingsView: View { private struct DisplaySettingsView: View { @AppStorage(AppDensity.userDefaultsKey) private var storedDensity = AppDensity.compact.rawValue + @AppStorage(AppAppearance.userDefaultsKey) private var storedAppearance = AppAppearance.system.rawValue var body: some View { Form { Section("Display") { + Picker("Appearance", selection: $storedAppearance) { + ForEach(AppAppearance.allCases) { appearance in + Text(appearance.title).tag(appearance.rawValue) + } + } + Picker("Density", selection: $storedDensity) { ForEach(AppDensity.allCases) { density in Text(density.title).tag(density.rawValue) @@ -2778,7 +2783,7 @@ private struct HistoryNetworkSettingsView: View { Text("History remains local-first. Auto-prune only trims older local snapshots on this device and defaults to unlimited.") .font(appDensity.font(.caption, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Section("Network") { @@ -2870,11 +2875,11 @@ private struct CloudSyncSettingsView: View { Text("iCloud Sync stores DomainDig data in your private iCloud account. DomainDig does not operate a sync server. Disabling sync keeps local data on this device.") .font(appDensity.font(.caption, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Text(cloudSyncService.detailMessage) .font(appDensity.font(.caption, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) if let lastErrorMessage = cloudSyncService.lastErrorMessage { Text(lastErrorMessage) @@ -2932,7 +2937,7 @@ private struct LocalAPISettingsView: View { if let statusMessage = localAPIService.statusMessage { Text(statusMessage) .font(appDensity.font(.caption, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } @@ -2947,7 +2952,7 @@ private struct LocalAPISettingsView: View { Text("Every request requires either `Authorization: Bearer <token>` or `X-API-Token`. DomainDig stores the token in Keychain and only binds the server to localhost.") .font(appDensity.font(.caption, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Section("Request Logging") { @@ -2962,7 +2967,7 @@ private struct LocalAPISettingsView: View { if localAPIService.requestLogs.isEmpty { Text("No local API requests logged yet.") .font(appDensity.font(.caption, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } else { ForEach(localAPIService.requestLogs.prefix(25)) { log in VStack(alignment: .leading, spacing: 4) { @@ -2977,11 +2982,11 @@ private struct LocalAPISettingsView: View { Text(log.timestamp.formatted(date: .abbreviated, time: .standard)) .font(appDensity.font(.caption2, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Text("\(Int(log.duration * 1000)) ms") .font(appDensity.font(.caption2, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } @@ -3184,13 +3189,13 @@ private struct MonitoringSettingsView: View { if let monitoringStatusMessage = viewModel.monitoringStatusMessage { Text(monitoringStatusMessage) .font(appDensity.font(.caption, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } if !FeatureAccessService.hasAccess(to: .automatedMonitoring) { Text("Background monitoring and alerts are available in Pro.") .font(appDensity.font(.caption, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } @@ -3260,7 +3265,7 @@ private struct DataPortabilitySettingsView: View { Text(importMode.explanation) .font(appDensity.font(.caption, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Button("Export Full Backup") { exportFullBackup() @@ -3317,12 +3322,12 @@ private struct DataPortabilitySettingsView: View { Text("Data stays on this device unless you export it. Backup files can include domain history, monitoring settings, and notes. Imported files are processed on-device.") .font(appDensity.font(.caption, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) if let portabilityStatusMessage = viewModel.portabilityStatusMessage { Text(portabilityStatusMessage) .font(appDensity.font(.caption, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } @@ -3331,7 +3336,7 @@ private struct DataPortabilitySettingsView: View { Section("Import Debug") { Text(importDebugStatus) .font(appDensity.font(.caption, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .textSelection(.enabled) } } @@ -3636,7 +3641,7 @@ private struct DataManagementSettingsView: View { if let deleteAllSuccessMessage { Text(deleteAllSuccessMessage) .font(.footnote.weight(.medium)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .padding(.horizontal, 14) .padding(.vertical, 10) .background(.thinMaterial, in: Capsule()) @@ -3722,7 +3727,7 @@ private struct DataImportPreviewSheet: View { Section("Warnings") { ForEach(preview.warnings, id: \.self) { warning in Text(warning) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } diff --git a/DomainDig/DashboardView.swift b/DomainDig/DashboardView.swift index da8d90a..8d41ccb 100644 --- a/DomainDig/DashboardView.swift +++ b/DomainDig/DashboardView.swift @@ -139,7 +139,7 @@ struct DashboardView: View { .font(appDensity.font(.headline, design: .default, weight: .semibold)) Text("\(group.domains.count) domain\(group.domains.count == 1 ? "" : "s")") .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Spacer() groupBadge(for: group.domains) @@ -170,7 +170,6 @@ struct DashboardView: View { } } } - .preferredColorScheme(.dark) } private func disclosureBinding(for apexDomain: String) -> Binding<Bool> { @@ -193,7 +192,7 @@ struct DashboardView: View { VStack(alignment: .leading, spacing: 8) { Text(title) .font(appDensity.font(.caption, design: .default, weight: .semibold)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Text("\(value)") .font(.system(size: 28, weight: .bold, design: .rounded)) .foregroundStyle(.primary) @@ -230,7 +229,7 @@ struct DashboardView: View { private func dashboardEmptyRow(_ message: String) -> some View { Text(message) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .padding(.vertical, 4) } @@ -288,7 +287,7 @@ private struct PortfolioActivityRow: View { Text(relativeTimestamp(item.timestamp)) } .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } .padding(.vertical, 4) @@ -320,10 +319,10 @@ private struct PortfolioAttentionRow: View { .foregroundStyle(.primary) Text(item.reason) .font(appDensity.font(.caption, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Text(relativeTimestamp(item.timestamp)) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } .padding(.vertical, 4) @@ -353,7 +352,7 @@ private struct PortfolioExpiryRow: View { .foregroundStyle(.primary) Text(expirySubtitle) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Spacer() AppStatusBadgeView(model: badgeModel) diff --git a/DomainDig/DomainCompareView.swift b/DomainDig/DomainCompareView.swift index 90ee5b1..8219fa7 100644 --- a/DomainDig/DomainCompareView.swift +++ b/DomainDig/DomainCompareView.swift @@ -38,7 +38,7 @@ struct DomainCompareView: View { Section { Text("Choose two different domains to compare.") .font(.callout) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } diff --git a/DomainDig/DomainDig/AuditViews.swift b/DomainDig/DomainDig/AuditViews.swift index df5a9a9..50ea137 100644 --- a/DomainDig/DomainDig/AuditViews.swift +++ b/DomainDig/DomainDig/AuditViews.swift @@ -41,13 +41,13 @@ struct AuditListView: View { Spacer() Text("\(group.sessions.count) audit\(group.sessions.count == 1 ? "" : "s")") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } if let latest = group.sessions.first { Text(latest.findings.isEmpty ? "No findings recorded yet" : latest.findings.map(\.title).prefix(2).joined(separator: " • ")) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .lineLimit(2) HStack(spacing: 8) { @@ -57,7 +57,7 @@ struct AuditListView: View { } Text("Checklist \(latest.completedChecklistCount)/\(latest.checklist.count)") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } @@ -85,7 +85,6 @@ struct AuditListView: View { } } } - .preferredColorScheme(.dark) } } @@ -116,7 +115,7 @@ struct AuditDomainTimelineView: View { Text("Reviewer: \(session.reviewer)") .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) HStack(spacing: 8) { Text("\(session.findings.count) findings") @@ -126,7 +125,7 @@ struct AuditDomainTimelineView: View { } } .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } .padding(.vertical, 4) } @@ -143,11 +142,11 @@ struct AuditDomainTimelineView: View { Spacer() Text("\(point.findingCount) findings") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Text("Open high severity: \(point.openHighSeverityCount) • Repeated issues: \(point.repeatedIssueCount)") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } .padding(.vertical, 2) } @@ -197,7 +196,7 @@ struct AuditSessionDetailView: View { .font(appDensity.font(.headline, design: .default, weight: .semibold)) Text("Reviewer: \(session.reviewer)") .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Spacer() auditStatusBadge(session.status) @@ -215,7 +214,7 @@ struct AuditSessionDetailView: View { Text("Captured \(session.evidence.capturedAt.formatted(date: .abbreviated, time: .shortened))") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } .listRowBackground(Color(.appSurface)) @@ -256,7 +255,7 @@ struct AuditSessionDetailView: View { .foregroundStyle(.primary) Text(item.detail) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Spacer() } @@ -270,7 +269,7 @@ struct AuditSessionDetailView: View { if session.findings.isEmpty { Text("No findings recorded.") .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } else { ForEach(session.findings) { finding in Button { @@ -286,11 +285,11 @@ struct AuditSessionDetailView: View { } Text(finding.summary) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .lineLimit(2) Text("\(finding.status.title) • \(finding.evidenceReferences.count) evidence refs") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } .padding(.vertical, 2) } @@ -321,7 +320,7 @@ struct AuditSessionDetailView: View { .font(appDensity.font(.caption, weight: .semibold)) Text(entry.changeSummaryMessage ?? "No change summary") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } @@ -337,11 +336,11 @@ struct AuditSessionDetailView: View { Spacer() Text(point.status.title) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Text("Findings \(point.findingCount) • Open high \(point.openHighSeverityCount) • Repeated \(point.repeatedIssueCount)") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } @@ -401,7 +400,6 @@ struct AuditSessionDetailView: View { .background(Color(.appBackground)) } } - .preferredColorScheme(.dark) } } @@ -459,7 +457,7 @@ private struct AuditFindingEditorView: View { ForEach(session.evidence.screenshots) { asset in Text("\(asset.title): \(asset.reference)") .font(.caption) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } diff --git a/DomainDig/DomainDigApp.swift b/DomainDig/DomainDigApp.swift index 2f2070c..d392dde 100644 --- a/DomainDig/DomainDigApp.swift +++ b/DomainDig/DomainDigApp.swift @@ -12,6 +12,7 @@ struct DomainDigApp: App { @UIApplicationDelegateAdaptor(DomainDigAppDelegate.self) private var appDelegate @Environment(\.scenePhase) private var scenePhase @AppStorage(AppDensity.userDefaultsKey) private var density = AppDensity.compact.rawValue + @AppStorage(AppAppearance.userDefaultsKey) private var appearance = AppAppearance.system.rawValue @State private var viewModel = DomainViewModel() @State private var purchaseService = PurchaseService.shared @State private var cloudSyncService = CloudSyncService.shared @@ -27,6 +28,8 @@ struct DomainDigApp: App { WindowGroup { RootTabView(viewModel: viewModel) .environment(\.appDensity, AppDensity(rawValue: density) ?? .compact) + // The single place appearance is applied. Keep it that way. + .preferredColorScheme((AppAppearance(rawValue: appearance) ?? .system).colorScheme) .task { let _ = purchaseService.currentTier let _ = cloudSyncService.status diff --git a/DomainDig/DomainDigUI.swift b/DomainDig/DomainDigUI.swift index 9c63e73..7d99e4e 100644 --- a/DomainDig/DomainDigUI.swift +++ b/DomainDig/DomainDigUI.swift @@ -6,6 +6,45 @@ import UIKit import AppKit #endif +/// User-selected appearance, applied once at the `WindowGroup`. +/// +/// Deliberately applied in exactly one place. The app previously carried 16 +/// separate `.preferredColorScheme(.dark)` calls scattered across view bodies, +/// which is how it became impossible to reach light mode at all — re-applying +/// per view is what let the lock spread unnoticed. +enum AppAppearance: String, CaseIterable, Identifiable { + case system + case light + case dark + + static let userDefaultsKey = "appAppearance" + + var id: String { rawValue } + + var title: String { + switch self { + case .system: + return "System" + case .light: + return "Light" + case .dark: + return "Dark" + } + } + + /// `nil` hands control back to the system setting. + var colorScheme: ColorScheme? { + switch self { + case .system: + return nil + case .light: + return .light + case .dark: + return .dark + } + } +} + enum AppDensity: String, CaseIterable, Identifiable { case compact case comfortable @@ -96,7 +135,7 @@ enum AppStatusFactory { case .registered: return .init(title: "Registered", systemImage: "circle.fill", foregroundColor: Color(.statusWarning), backgroundColor: Color(.statusWarning).opacity(0.16)) case .unknown, .none: - return .init(title: "Unknown", systemImage: "questionmark.circle", foregroundColor: .secondary, backgroundColor: Color(.appSurfaceElevated)) + return .init(title: "Unknown", systemImage: "questionmark.circle", foregroundColor: Color(.appTextSecondary), backgroundColor: Color(.appSurfaceElevated)) } } @@ -112,7 +151,7 @@ enum AppStatusFactory { static func email(_ result: EmailSecurityResult?, error: String?) -> AppStatusBadgeModel { guard error == nil, let result else { - return .init(title: "Missing", systemImage: "minus.circle", foregroundColor: .secondary, backgroundColor: Color(.appSurfaceElevated)) + return .init(title: "Missing", systemImage: "minus.circle", foregroundColor: Color(.appTextSecondary), backgroundColor: Color(.appSurfaceElevated)) } let foundCount = [result.spf.found, result.dmarc.found, result.dkim.found].filter { $0 }.count @@ -122,18 +161,18 @@ enum AppStatusFactory { case 1, 2: return .init(title: "Partial", systemImage: "shield.lefthalf.filled", foregroundColor: Color(.statusWarning), backgroundColor: Color(.statusWarning).opacity(0.16)) default: - return .init(title: "Missing", systemImage: "minus.circle", foregroundColor: .secondary, backgroundColor: Color(.appSurfaceElevated)) + return .init(title: "Missing", systemImage: "minus.circle", foregroundColor: Color(.appTextSecondary), backgroundColor: Color(.appSurfaceElevated)) } } static func change(_ summary: DomainChangeSummary?) -> AppStatusBadgeModel { guard let summary else { - return .init(title: "Unchanged", systemImage: "circle", foregroundColor: .secondary, backgroundColor: Color(.appSurfaceElevated)) + return .init(title: "Unchanged", systemImage: "circle", foregroundColor: Color(.appTextSecondary), backgroundColor: Color(.appSurfaceElevated)) } if summary.hasChanges { return .init(title: "Changed", systemImage: "arrow.triangle.2.circlepath", foregroundColor: Color(.statusInfo), backgroundColor: Color(.statusInfo).opacity(0.16)) } - return .init(title: "Unchanged", systemImage: "checkmark.circle", foregroundColor: .secondary, backgroundColor: Color(.appSurfaceElevated)) + return .init(title: "Unchanged", systemImage: "checkmark.circle", foregroundColor: Color(.appTextSecondary), backgroundColor: Color(.appSurfaceElevated)) } } @@ -259,7 +298,7 @@ struct EmptyStateCardView: View { Text(message) .font(appDensity.font(.body)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .fixedSize(horizontal: false, vertical: true) Text(suggestion) @@ -311,14 +350,14 @@ struct CollapsibleSectionView<HeaderTrailing: View, Content: View>: View { if let subtitle { Text(subtitle) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } Spacer(minLength: 8) trailing() Image(systemName: isCollapsed ? "chevron.down" : "chevron.up") .font(.caption.weight(.semibold)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } .contentShape(Rectangle()) .frame(minHeight: appDensity.metrics.controlMinHeight, alignment: .center) diff --git a/DomainDig/HistoryView.swift b/DomainDig/HistoryView.swift index ffa7745..5b788c3 100644 --- a/DomainDig/HistoryView.swift +++ b/DomainDig/HistoryView.swift @@ -40,12 +40,12 @@ struct HistoryView: View { Spacer() Text("\(item.count) snapshots") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Text(item.latest.changeSummaryMessage ?? "No change summary") .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .lineLimit(2) HStack(spacing: 8) { @@ -129,7 +129,6 @@ struct HistoryView: View { .onChange(of: viewModel.rerunNavigationToken) { _, _ in dismiss() } - .preferredColorScheme(.dark) } private func deleteFilteredHistoryEntries(at offsets: IndexSet) { @@ -372,7 +371,6 @@ struct HistoryDetailView: View { } } } - .preferredColorScheme(.dark) } private var snapshotBanner: some View { @@ -385,7 +383,7 @@ struct HistoryDetailView: View { Spacer() Text("Live re-run available") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } if let mismatchNote = viewModel.resolverMismatchNote(for: entry) { Text(mismatchNote) @@ -400,10 +398,10 @@ struct HistoryDetailView: View { if let note = entry.note, !note.isEmpty { Text(note) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .padding(8) .frame(maxWidth: .infinity, alignment: .leading) .background(Color(.appSurface)) diff --git a/DomainDig/IntegrationsView.swift b/DomainDig/IntegrationsView.swift index 163d4d1..bb9abae 100644 --- a/DomainDig/IntegrationsView.swift +++ b/DomainDig/IntegrationsView.swift @@ -15,7 +15,7 @@ struct IntegrationsSettingsView: View { if let statusMessage = integrationService.statusMessage { Text(statusMessage) .font(.caption) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Button("Process Queue Now") { @@ -26,7 +26,7 @@ struct IntegrationsSettingsView: View { Section("Targets") { if integrationService.targets.isEmpty { Text("No integrations configured.") - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } else { ForEach(integrationService.targets) { target in NavigationLink { @@ -42,12 +42,12 @@ struct IntegrationsSettingsView: View { Text(target.name) Spacer() Text(target.type.title) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Text(summary(for: target)) .font(.caption) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) if !target.isEnabled { Text("Disabled") @@ -138,7 +138,7 @@ private struct IntegrationDetailView: View { Section("Delivery Log") { if integrationService.deliveryRecords(for: target.id).isEmpty { Text("No deliveries yet.") - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } else { ForEach(integrationService.deliveryRecords(for: target.id), id: \.id) { record in VStack(alignment: .leading, spacing: 4) { @@ -147,7 +147,7 @@ private struct IntegrationDetailView: View { Spacer() Text(record.timestamp.formatted(date: .abbreviated, time: .shortened)) .font(.caption) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Text(record.summary) @@ -155,7 +155,7 @@ private struct IntegrationDetailView: View { Text(record.destination) .font(.caption) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) if let failureReason = record.failureReason { let failureColor: Color = record.status == .skipped ? .secondary : Color(.statusCritical) @@ -169,7 +169,7 @@ private struct IntegrationDetailView: View { } } else { Text("Integration not found.") - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } .navigationTitle(target?.name ?? "Integration") @@ -265,7 +265,7 @@ private struct IntegrationEditorView: View { if existingTarget != nil { Text("Saved webhook URL remains in Keychain unless you replace it.") .font(.caption) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } case .slack: @@ -278,7 +278,7 @@ private struct IntegrationEditorView: View { if existingTarget != nil { Text("Saved Slack webhook remains in Keychain unless you replace it.") .font(.caption) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } case .email: @@ -315,7 +315,7 @@ private struct IntegrationEditorView: View { if existingTarget != nil { Text("Saved SMTP password remains in Keychain unless you replace it.") .font(.caption) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } diff --git a/DomainDig/MonitoringView.swift b/DomainDig/MonitoringView.swift index decad35..db6f685 100644 --- a/DomainDig/MonitoringView.swift +++ b/DomainDig/MonitoringView.swift @@ -31,7 +31,7 @@ struct MonitoringView: View { !monitoringStatusMessage.isEmpty { Text(monitoringStatusMessage) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Button(viewModel.monitoringRunInProgress ? "Monitoring…" : "Run Now") { @@ -68,12 +68,12 @@ struct MonitoringView: View { Spacer() Text(log.timestamp.formatted(date: .abbreviated, time: .shortened)) .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Text(log.summary) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) HStack(spacing: 8) { metricBadge(title: "\(log.domainsChecked) checked") @@ -95,7 +95,6 @@ struct MonitoringView: View { .scrollContentBackground(.hidden) .background(Color(.appBackground)) .navigationTitle("Monitoring") - .preferredColorScheme(.dark) .onAppear { viewModel.refreshMonitoringState() } @@ -152,7 +151,7 @@ struct MonitoringLogDetailView: View { Text(result.summaryMessage) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) HStack(spacing: 10) { Text(result.resultSource.label) @@ -162,7 +161,7 @@ struct MonitoringLogDetailView: View { } } .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) if let errorMessage = result.errorMessage { Text(errorMessage) @@ -188,7 +187,7 @@ struct MonitoringLogDetailView: View { ForEach(log.errors, id: \.self) { error in Text(error) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } .listRowBackground(Color(.appSurface)) @@ -197,7 +196,6 @@ struct MonitoringLogDetailView: View { .scrollContentBackground(.hidden) .background(Color(.appBackground)) .navigationTitle("Run Details") - .preferredColorScheme(.dark) } private func color(for severity: MonitoringAlertSeverity) -> Color { diff --git a/DomainDig/PaywallView.swift b/DomainDig/PaywallView.swift index 27e80ee..2b66037 100644 --- a/DomainDig/PaywallView.swift +++ b/DomainDig/PaywallView.swift @@ -12,7 +12,7 @@ struct PaywallView: View { Section { Text("Pro unlocks workflows, scale, monitoring automation, and exports. Pro+ adds deeper external intelligence with no additional usage limits.") .font(appDensity.font(.body, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .fixedSize(horizontal: false, vertical: true) } @@ -38,7 +38,7 @@ struct PaywallView: View { } else if purchaseService.products.isEmpty { Text("Pricing is unavailable right now.") .font(appDensity.font(.caption, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Button("Retry") { Task { @@ -58,7 +58,7 @@ struct PaywallView: View { .foregroundStyle(.primary) Text(product.displayPrice) .font(appDensity.font(.callout, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } .frame(maxWidth: .infinity, alignment: .leading) } @@ -69,7 +69,7 @@ struct PaywallView: View { if let statusMessage = purchaseService.statusMessage { Text(statusMessage) .font(appDensity.font(.caption, design: .default)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } if let errorMessage = purchaseService.errorMessage { diff --git a/DomainDig/SavedDomainsView.swift b/DomainDig/SavedDomainsView.swift index 59dcb73..125d8a0 100644 --- a/DomainDig/SavedDomainsView.swift +++ b/DomainDig/SavedDomainsView.swift @@ -9,7 +9,7 @@ struct SavedDomainsView: View { if viewModel.savedDomains.isEmpty { Text("No saved domains") .font(.system(.callout, design: .monospaced)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .listRowBackground(Color(.appSurface)) } else { ForEach(viewModel.savedDomains, id: \.self) { domain in @@ -37,6 +37,5 @@ struct SavedDomainsView: View { EditButton() } } - .preferredColorScheme(.dark) } } diff --git a/DomainDig/ScheduledReportsView.swift b/DomainDig/ScheduledReportsView.swift index dffafe3..5c0a14a 100644 --- a/DomainDig/ScheduledReportsView.swift +++ b/DomainDig/ScheduledReportsView.swift @@ -13,7 +13,7 @@ struct ScheduledReportsView: View { if !FeatureAccessService.hasAccess(to: .automatedMonitoring) { Text("Scheduled reports require Pro.") .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Toggle("Scheduled Reports", isOn: Binding( @@ -59,7 +59,7 @@ struct ScheduledReportsView: View { if let statusMessage { Text(statusMessage) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Button(isGenerating ? "Generating…" : "Generate Now") { @@ -91,7 +91,7 @@ struct ScheduledReportsView: View { .foregroundStyle(.primary) Text(log.generatedAt.formatted(date: .abbreviated, time: .shortened)) .font(.caption) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } diff --git a/DomainDig/TimelineView.swift b/DomainDig/TimelineView.swift index 052b088..16e2bf8 100644 --- a/DomainDig/TimelineView.swift +++ b/DomainDig/TimelineView.swift @@ -125,7 +125,7 @@ private struct TimelineRow: View { Text(summary.changeSummaryMessage ?? "No change summary") .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .lineLimit(2) HStack(spacing: 8) { @@ -137,14 +137,14 @@ private struct TimelineRow: View { } } .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) if !entry.intelligenceTimeline.isEmpty { VStack(alignment: .leading, spacing: 4) { ForEach(Array(entry.intelligenceTimeline.prefix(2))) { event in Text("\(event.title): \(event.detail)") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .lineLimit(1) } } @@ -161,7 +161,7 @@ private struct TimelineRow: View { .lineLimit(1) } .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } diff --git a/DomainDig/WatchlistView.swift b/DomainDig/WatchlistView.swift index 5c5609e..ddb6f4a 100644 --- a/DomainDig/WatchlistView.swift +++ b/DomainDig/WatchlistView.swift @@ -42,7 +42,7 @@ struct WatchlistView: View { HStack { Text(viewModel.batchProgressLabel) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Spacer() if viewModel.batchLookupRunning { Button("Cancel") { @@ -78,7 +78,7 @@ struct WatchlistView: View { Section { Text(limitMessage) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } .listRowBackground(Color(.appSurface)) } @@ -208,7 +208,7 @@ struct WatchlistView: View { Section { Text("Adds the domain directly to your watchlist so monitoring can run without a prior inspection.") .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } if let addDomainError { @@ -267,7 +267,7 @@ struct WatchlistView: View { .foregroundStyle(.primary) Text([view.tag, view.filter.title, view.sort.title].compactMap { $0 }.joined(separator: " • ")) .font(.caption) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } @@ -288,7 +288,6 @@ struct WatchlistView: View { } } } - .preferredColorScheme(.dark) } @ViewBuilder @@ -450,12 +449,12 @@ struct WatchlistRowView: View { Text("Updated \(trackedDomain.updatedAt.formatted(date: .abbreviated, time: .shortened))") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) if let collaboration = trackedDomain.collaboration, collaboration.isShared { Text("\(collaboration.ownership.title) • \(collaboration.permission.title)") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } HStack(spacing: 8) { @@ -468,19 +467,19 @@ struct WatchlistRowView: View { } } .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) indicatorRow if let note = trackedDomain.note?.trimmingCharacters(in: .whitespacesAndNewlines), !note.isEmpty { Text(note) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .lineLimit(2) } else if let summary = trackedDomain.lastChangeSummary { Text(summary.message) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .lineLimit(2) } } @@ -502,7 +501,7 @@ struct WatchlistRowView: View { @ViewBuilder private var statusBadge: some View { if isRefreshing { - AppStatusBadgeView(model: .init(title: "Refreshing", systemImage: "arrow.clockwise", foregroundColor: .secondary, backgroundColor: Color(.appSurfaceElevated))) + AppStatusBadgeView(model: .init(title: "Refreshing", systemImage: "arrow.clockwise", foregroundColor: Color(.appTextSecondary), backgroundColor: Color(.appSurfaceElevated))) } else { AppStatusBadgeView(model: AppStatusFactory.availability(trackedDomain.lastKnownAvailability)) } @@ -700,7 +699,7 @@ struct TrackedDomainDetailView: View { if latestSnapshots.isEmpty { Text("No snapshots yet") .font(.system(.caption, design: .monospaced)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } else { ForEach(latestSnapshots) { entry in NavigationLink { @@ -712,7 +711,7 @@ struct TrackedDomainDetailView: View { .foregroundStyle(.primary) Text(entry.changeSummary?.hasChanges == true ? "Changed" : "Snapshot") .font(.system(.caption2, design: .monospaced)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } @@ -723,7 +722,6 @@ struct TrackedDomainDetailView: View { .scrollContentBackground(.hidden) .background(Color(.appBackground)) .navigationTitle(liveTrackedDomain.domain) - .preferredColorScheme(.dark) .onChange(of: viewModel.rerunNavigationToken) { _, _ in dismiss() } diff --git a/DomainDig/WorkflowsView.swift b/DomainDig/WorkflowsView.swift index 2195af5..d628ba4 100644 --- a/DomainDig/WorkflowsView.swift +++ b/DomainDig/WorkflowsView.swift @@ -22,7 +22,7 @@ struct WorkflowsView: View { Section { Text(limitMessage) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } .listRowBackground(Color(.appSurface)) } @@ -54,7 +54,6 @@ struct WorkflowsView: View { .sheet(item: workflowSummaryBinding) { summary in WorkflowRunSummaryView(viewModel: viewModel, summary: summary) } - .preferredColorScheme(.dark) } private var workflowSummaryBinding: Binding<WorkflowRunSummary?> { @@ -78,7 +77,7 @@ struct WorkflowsView: View { HStack { Text(viewModel.batchProgressLabel) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Spacer() if viewModel.batchLookupRunning { Button("Cancel") { @@ -142,23 +141,23 @@ private struct WorkflowRowView: View { Spacer(minLength: 8) Text("\(workflow.domains.count) domains") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Text("Updated \(workflow.updatedAt.formatted(date: .abbreviated, time: .shortened))") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) if let collaboration = workflow.collaboration, collaboration.isShared { Text("\(collaboration.ownership.title) • \(collaboration.permission.title)") .font(appDensity.font(.caption2)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } if let notes = workflow.notes, !notes.isEmpty { Text(notes) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .lineLimit(2) } } @@ -197,12 +196,12 @@ struct WorkflowDetailView: View { if let notes = workflow.notes, !notes.isEmpty { Text(notes) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } if let collaboration = workflow.collaboration, collaboration.isShared { Text("\(collaboration.ownership.title) • \(collaboration.permission.title)") .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } @@ -277,7 +276,7 @@ struct WorkflowDetailView: View { if !latestSummary.workflowInsights.isEmpty { Text(latestSummary.workflowInsights[0].description) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Button { @@ -306,7 +305,7 @@ struct WorkflowDetailView: View { if workflow.domains.isEmpty { Text("No domains in this workflow") .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } else { ForEach(workflow.domains, id: \.self) { domain in Button { @@ -318,7 +317,7 @@ struct WorkflowDetailView: View { .foregroundStyle(.primary) Spacer() Image(systemName: "arrow.up.right.circle") - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } .buttonStyle(.plain) @@ -355,17 +354,16 @@ struct WorkflowDetailView: View { } else { Text("Workflow not found") .font(appDensity.font(.callout)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } - .preferredColorScheme(.dark) } private func statRow(label: String, value: String) -> some View { HStack { Text(label) .font(appDensity.font(.caption)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Spacer() Text(value) .font(appDensity.font(.callout)) @@ -449,7 +447,6 @@ struct WorkflowComposerView: View { } } } - .preferredColorScheme(.dark) } private var parsedDomains: [String] { @@ -507,7 +504,7 @@ struct WorkflowRunSummaryView: View { .foregroundStyle(.primary) Text(insight.domainsInvolved.joined(separator: ", ")) .font(.system(.caption, design: .monospaced)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } } @@ -517,7 +514,7 @@ struct WorkflowRunSummaryView: View { if visibleResults.isEmpty { Text("No domains with meaningful changes or warnings") .font(.system(.caption, design: .monospaced)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } else { ForEach(visibleResults) { result in if let entry = viewModel.historyEntry(for: result) { @@ -578,14 +575,13 @@ struct WorkflowRunSummaryView: View { } } } - .preferredColorScheme(.dark) } private func statRow(label: String, value: String) -> some View { HStack { Text(label) .font(.system(.caption, design: .monospaced)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Spacer() Text(value) .font(.system(.callout, design: .monospaced)) @@ -687,7 +683,6 @@ struct WorkflowBulkAddSheet: View { } } } - .preferredColorScheme(.dark) } private var selectedDomainList: [String] { diff --git a/DomainDigWidget/DomainDigPortfolioWidget.swift b/DomainDigWidget/DomainDigPortfolioWidget.swift index a3335c8..37a96a0 100644 --- a/DomainDigWidget/DomainDigPortfolioWidget.swift +++ b/DomainDigWidget/DomainDigPortfolioWidget.swift @@ -61,10 +61,10 @@ struct DomainDigWidgetView: View { VStack(spacing: 6) { Image(systemName: "magnifyingglass") .font(.title2) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Text("No tracked domains") .font(.caption) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .multilineTextAlignment(.center) } } @@ -80,13 +80,13 @@ struct DomainDigWidgetView: View { Spacer() } .font(.caption2) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Text("\(data.totalDomains)") .font(.system(size: 34, weight: .bold, design: .rounded)) Text("tracked") .font(.caption2) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Spacer(minLength: 0) @@ -113,11 +113,11 @@ struct DomainDigWidgetView: View { Label("Domain Portfolio", systemImage: "shield.lefthalf.filled") .font(.caption) .fontWeight(.semibold) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Spacer() Text("\(data.totalDomains) tracked") .font(.caption2) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } HStack(spacing: 12) { @@ -147,7 +147,7 @@ struct DomainDigWidgetView: View { .foregroundStyle(color) Text(label) .font(.system(size: 9)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } .frame(maxWidth: .infinity, alignment: .leading) } @@ -160,7 +160,7 @@ struct DomainDigWidgetView: View { if domain.isPinned { Image(systemName: "pin.fill") .font(.system(size: 8)) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Text(domain.domain) .font(.caption) @@ -168,7 +168,7 @@ struct DomainDigWidgetView: View { Spacer(minLength: 4) Text(certLabel(for: domain)) .font(.caption2) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } } diff --git a/DomainDigWidget/SweepLiveActivity.swift b/DomainDigWidget/SweepLiveActivity.swift index b96f8bf..900ebc1 100644 --- a/DomainDigWidget/SweepLiveActivity.swift +++ b/DomainDigWidget/SweepLiveActivity.swift @@ -14,7 +14,7 @@ struct SweepLiveActivity: Widget { DynamicIslandExpandedRegion(.leading) { Label(context.attributes.title, systemImage: "arrow.trianglehead.2.clockwise") .font(.caption) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } DynamicIslandExpandedRegion(.trailing) { Text("\(context.state.completed)/\(context.state.total)") @@ -28,7 +28,7 @@ struct SweepLiveActivity: Widget { if let current = context.state.currentDomain { Text(current) .font(.caption2) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .lineLimit(1) } } @@ -58,7 +58,7 @@ private struct SweepActivityBannerView: View { Label(context.attributes.title, systemImage: "arrow.trianglehead.2.clockwise") .font(.caption) .fontWeight(.semibold) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) Spacer() Text("\(context.state.completed) of \(context.state.total)") .font(.caption) @@ -72,12 +72,12 @@ private struct SweepActivityBannerView: View { if let current = context.state.currentDomain { Text(current) .font(.caption2) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) .lineLimit(1) } else { Text(context.state.completed >= context.state.total ? "Finished" : "Working…") .font(.caption2) - .foregroundStyle(.secondary) + .foregroundStyle(Color(.appTextSecondary)) } Spacer() if context.state.changed > 0 { diff --git a/Shared/Colors.xcassets/AppTextSecondary.colorset/Contents.json b/Shared/Colors.xcassets/AppTextSecondary.colorset/Contents.json new file mode 100644 index 0000000..9e7bb36 --- /dev/null +++ b/Shared/Colors.xcassets/AppTextSecondary.colorset/Contents.json @@ -0,0 +1,78 @@ +{ + "colors": [ + { + "idiom": "universal", + "color": { + "color-space": "srgb", + "components": { + "red": "0x5A", + "green": "0x5A", + "blue": "0x5F", + "alpha": "1.000" + } + } + }, + { + "idiom": "universal", + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "0xA1", + "green": "0xA1", + "blue": "0xA6", + "alpha": "1.000" + } + } + }, + { + "idiom": "universal", + "appearances": [ + { + "appearance": "contrast", + "value": "high" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "0x3A", + "green": "0x3A", + "blue": "0x3C", + "alpha": "1.000" + } + } + }, + { + "idiom": "universal", + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + }, + { + "appearance": "contrast", + "value": "high" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "0xC7", + "green": "0xC7", + "blue": "0xCC", + "alpha": "1.000" + } + } + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} |
