summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-07-20 19:14:04 -0500
committerChristian Cleberg <[email protected]>2026-07-20 21:03:10 -0500
commit2c608ed9cb11f3e835f577994b5627b9b7910f4f (patch)
tree078ff50c5151fcc5e91b9d28051562f0bc28088d
parent349bec0e99f330fc1172ca9847c58f019e861f61 (diff)
downloaddomain-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.md26
-rw-r--r--DomainDig/AuditModeView.swift4
-rw-r--r--DomainDig/BatchResultsView.swift16
-rw-r--r--DomainDig/BatchSweepSummaryView.swift4
-rw-r--r--DomainDig/ContentView.swift149
-rw-r--r--DomainDig/DashboardView.swift15
-rw-r--r--DomainDig/DomainCompareView.swift2
-rw-r--r--DomainDig/DomainDig/AuditViews.swift36
-rw-r--r--DomainDig/DomainDigApp.swift3
-rw-r--r--DomainDig/DomainDigUI.swift55
-rw-r--r--DomainDig/HistoryView.swift12
-rw-r--r--DomainDig/IntegrationsView.swift22
-rw-r--r--DomainDig/MonitoringView.swift14
-rw-r--r--DomainDig/PaywallView.swift8
-rw-r--r--DomainDig/SavedDomainsView.swift3
-rw-r--r--DomainDig/ScheduledReportsView.swift6
-rw-r--r--DomainDig/TimelineView.swift8
-rw-r--r--DomainDig/WatchlistView.swift26
-rw-r--r--DomainDig/WorkflowsView.swift37
-rw-r--r--DomainDigWidget/DomainDigPortfolioWidget.swift18
-rw-r--r--DomainDigWidget/SweepLiveActivity.swift10
-rw-r--r--Shared/Colors.xcassets/AppTextSecondary.colorset/Contents.json78
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
+ }
+}