diff options
| -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 + } +} |
