krz/hutch

an ios client for sourcehut

clone: git clone https://gitbay.org/krz/hutch.git

a6ad2b9815d72575206bdca860eef9db850cdf1c

verified · cmc

author: Christian Cleberg <hello@cleberg.net> · 2026-04-14T00:06:54Z

feat(theme): AMOLED true-black rows via themedRow()

Apply listRowBackground(Color.black) per row in AMOLED mode using ThemedRowStyle
and themedRow() across Lists and Forms. Use themedList() for scroll/grouped
backgrounds. Treat segmented and clear list rows with isAMOLED-aware
listRowBackground where Color.clear was required. Removes reliance on UIKit
appearance for list cells on iOS 16+.

Implements: https://todo.sr.ht/~ccleberg/hutch/24
 Hutch.xcodeproj/project.pbxproj                    | 16 +++----
 Hutch/App/HutchApp.swift                           |  2 +
 Hutch/App/RootView.swift                           | 21 ++++++++++
 Hutch/App/ThemeManager.swift                       | 49 +++++++++++++++++++++-
 Hutch/Views/Auth/AuthView.swift                    |  7 ++++
 Hutch/Views/Builds/BuildDetailView.swift           | 25 +++++++++++
 Hutch/Views/Builds/BuildListView.swift             | 14 ++++++-
 Hutch/Views/Home/HomeView.swift                    |  6 +++
 Hutch/Views/Inbox/ThreadDetailView.swift           |  6 +++
 Hutch/Views/Lists/MailingListListView.swift        |  2 +
 Hutch/Views/Lookup/LookupView.swift                |  5 +++
 Hutch/Views/Lookup/UserProfileView.swift           | 24 ++++++++++-
 Hutch/Views/Lookup/UserRepositoriesView.swift      |  2 +
 Hutch/Views/Lookup/UserTrackersView.swift          |  2 +
 Hutch/Views/More/AccountSwitcherView.swift         |  3 ++
 Hutch/Views/More/AddAccountView.swift              |  3 ++
 Hutch/Views/More/ManPageBrowserView.swift          |  2 +
 Hutch/Views/More/MoreView.swift                    |  9 ++++
 Hutch/Views/More/ProfileView.swift                 | 31 +++++++++++++-
 Hutch/Views/Pastes/PasteDetailView.swift           |  8 ++++
 Hutch/Views/Pastes/PasteListView.swift             |  8 ++++
 Hutch/Views/Projects/ProjectDetailView.swift       |  6 +++
 Hutch/Views/Projects/ProjectMailingListView.swift  |  2 +
 Hutch/Views/Projects/ProjectsListView.swift        |  1 +
 Hutch/Views/Repositories/ArtifactsView.swift       |  2 +
 Hutch/Views/Repositories/CommitLogView.swift       |  3 ++
 Hutch/Views/Repositories/FileTreeView.swift        |  6 +++
 .../Repositories/HgRepositoryDetailView.swift      | 12 ++++++
 .../Repositories/HgRepositorySettingsView.swift    | 19 +++++++++
 Hutch/Views/Repositories/ReferencesListView.swift  |  3 ++
 Hutch/Views/Repositories/RepositoryACLView.swift   |  9 +++-
 Hutch/Views/Repositories/RepositoryListView.swift  | 11 +++++
 .../Repositories/RepositorySettingsView.swift      | 19 +++++++++
 Hutch/Views/Settings/SettingsView.swift            | 23 ++++++++++
 Hutch/Views/SystemStatus/SystemStatusView.swift    |  7 ++++
 Hutch/Views/Tickets/TicketDetailView.swift         | 13 ++++++
 Hutch/Views/Tickets/TicketListView.swift           | 23 +++++++++-
 Hutch/Views/Tickets/TrackerListView.swift          |  3 ++
 Hutch/Views/Tickets/TrackerManagementView.swift    | 37 ++++++++++++++++
 Hutch/Views/Work/WorkView.swift                    | 11 ++++-
 40 files changed, 439 insertions(+), 16 deletions(-)

diff --git a/Hutch.xcodeproj/project.pbxproj b/Hutch.xcodeproj/project.pbxproj
index dda781d..befab2b 100644
--- a/Hutch.xcodeproj/project.pbxproj
+++ b/Hutch.xcodeproj/project.pbxproj
@@ -515,7 +515,7 @@
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 67;
+				CURRENT_PROJECT_VERSION = 68;
 				DEVELOPMENT_TEAM = ZCNAX3VL9D;
 				ENABLE_PREVIEWS = YES;
 				GENERATE_INFOPLIST_FILE = YES;
@@ -532,7 +532,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 3.0.6;
+				MARKETING_VERSION = 3.1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -552,7 +552,7 @@
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 67;
+				CURRENT_PROJECT_VERSION = 68;
 				DEVELOPMENT_TEAM = ZCNAX3VL9D;
 				ENABLE_PREVIEWS = YES;
 				GENERATE_INFOPLIST_FILE = YES;
@@ -569,7 +569,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 3.0.6;
+				MARKETING_VERSION = 3.1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -632,7 +632,7 @@
 				APPLICATION_EXTENSION_API_ONLY = YES;
 				CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 67;
+				CURRENT_PROJECT_VERSION = 68;
 				DEVELOPMENT_TEAM = ZCNAX3VL9D;
 				GENERATE_INFOPLIST_FILE = NO;
 				INFOPLIST_FILE = HutchWidgetExtension/Info.plist;
@@ -642,7 +642,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 3.0.6;
+				MARKETING_VERSION = 3.1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
@@ -661,7 +661,7 @@
 				APPLICATION_EXTENSION_API_ONLY = YES;
 				CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 67;
+				CURRENT_PROJECT_VERSION = 68;
 				DEVELOPMENT_TEAM = ZCNAX3VL9D;
 				GENERATE_INFOPLIST_FILE = NO;
 				INFOPLIST_FILE = HutchWidgetExtension/Info.plist;
@@ -671,7 +671,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 3.0.6;
+				MARKETING_VERSION = 3.1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
diff --git a/Hutch/App/HutchApp.swift b/Hutch/App/HutchApp.swift
index 0fb1875..9c643d9 100644
--- a/Hutch/App/HutchApp.swift
+++ b/Hutch/App/HutchApp.swift
@@ -13,7 +13,9 @@ struct HutchApp: App {
                 .environment(appState)
                 .environment(networkMonitor)
                 .environment(\.displayDensity, displayDensity)
+                .environment(\.isAMOLEDTheme, appTheme == .amoled)
                 .environment(\.defaultMinListRowHeight, displayDensity == .compact ? 36 : 44)
+                .background(appTheme == .amoled ? Color.black : Color.clear)
                 .preferredColorScheme(appTheme.colorScheme)
                 .onOpenURL { url in
                     if let link = DeepLink(url: url) {
diff --git a/Hutch/App/RootView.swift b/Hutch/App/RootView.swift
index b4cf3bd..ed029bb 100644
--- a/Hutch/App/RootView.swift
+++ b/Hutch/App/RootView.swift
@@ -4,6 +4,7 @@ import SwiftUI
 /// full-screen sheet for token entry on first launch.
 struct RootView: View {
     @Environment(AppState.self) private var appState
+    @Environment(\.isAMOLEDTheme) private var isAMOLED
     @State private var homePath = NavigationPath()
     @State private var morePath = NavigationPath()
     @State private var repoPath = NavigationPath()
@@ -125,6 +126,7 @@ struct RootView: View {
         .id(appState.sessionIdentity)
         .defaultAppStorage(appState.accountDefaults)
         .modifier(SidebarAdaptableTabStyle())
+        .modifier(AMOLEDToolbarStyle(isAMOLED: isAMOLED))
         .modifier(TabKeyboardShortcuts(selectedTab: Binding(
             get: { appState.selectedTab },
             set: { appState.selectedTab = $0 }
@@ -427,6 +429,25 @@ private struct TabKeyboardShortcuts: ViewModifier {
     }
 }
 
+// MARK: - AMOLED Toolbar Styling
+
+/// Applies true-black backgrounds to the tab bar and navigation bar when the AMOLED theme is active.
+private struct AMOLEDToolbarStyle: ViewModifier {
+    let isAMOLED: Bool
+
+    func body(content: Content) -> some View {
+        if isAMOLED {
+            content
+                .toolbarBackground(Color.black, for: .tabBar)
+                .toolbarBackground(.visible, for: .tabBar)
+                .toolbarBackground(Color.black, for: .navigationBar)
+                .toolbarBackground(.visible, for: .navigationBar)
+        } else {
+            content
+        }
+    }
+}
+
 // MARK: - iPad Sidebar Adaptable
 
 /// Applies `.tabViewStyle(.sidebarAdaptable)` on iOS 18+ so the tab bar
diff --git a/Hutch/App/ThemeManager.swift b/Hutch/App/ThemeManager.swift
index ee74921..af54094 100644
--- a/Hutch/App/ThemeManager.swift
+++ b/Hutch/App/ThemeManager.swift
@@ -6,6 +6,7 @@ enum AppTheme: String, CaseIterable, Identifiable {
     case system
     case light
     case dark
+    case amoled
 
     var id: String { rawValue }
 
@@ -14,6 +15,7 @@ enum AppTheme: String, CaseIterable, Identifiable {
         case .system: "System"
         case .light: "Light"
         case .dark: "Dark"
+        case .amoled: "AMOLED"
         }
     }
 
@@ -22,6 +24,7 @@ enum AppTheme: String, CaseIterable, Identifiable {
         case .system: nil
         case .light: .light
         case .dark: .dark
+        case .amoled: .dark
         }
     }
 }
@@ -46,25 +49,51 @@ private struct DisplayDensityKey: EnvironmentKey {
     static let defaultValue: DisplayDensity = .standard
 }
 
+private struct IsAMOLEDThemeKey: EnvironmentKey {
+    static let defaultValue: Bool = false
+}
+
 extension EnvironmentValues {
     var displayDensity: DisplayDensity {
         get { self[DisplayDensityKey.self] }
         set { self[DisplayDensityKey.self] = newValue }
     }
+
+    var isAMOLEDTheme: Bool {
+        get { self[IsAMOLEDThemeKey.self] }
+        set { self[IsAMOLEDThemeKey.self] = newValue }
+    }
 }
 
 // MARK: - Themed List Modifier
 
-/// Combined modifier for List/Form that applies compact section spacing.
+/// Combined modifier for List/Form that applies compact section spacing and AMOLED black backgrounds.
 /// Apply once per List or Form.
 struct ThemedListStyle: ViewModifier {
     @Environment(\.displayDensity) private var density
+    @Environment(\.isAMOLEDTheme) private var isAMOLED
 
     func body(content: Content) -> some View {
         content
             .listSectionSpacing(density == .compact ? .compact : .default)
             .contentMargins(.vertical, density == .compact ? 2 : 8, for: .scrollContent)
             .controlSize(density == .compact ? .small : .regular)
+            .modifier(AMOLEDListBackground(isAMOLED: isAMOLED))
+    }
+}
+
+/// Hides the default grouped list background and replaces it with true black for OLED screens.
+private struct AMOLEDListBackground: ViewModifier {
+    let isAMOLED: Bool
+
+    func body(content: Content) -> some View {
+        if isAMOLED {
+            content
+                .scrollContentBackground(.hidden)
+                .background(Color.black)
+        } else {
+            content
+        }
     }
 }
 
@@ -74,3 +103,21 @@ extension View {
         modifier(ThemedListStyle())
     }
 }
+
+// MARK: - Themed Row Modifier
+
+struct ThemedRowStyle: ViewModifier {
+    @Environment(\.isAMOLEDTheme) private var isAMOLED
+
+    func body(content: Content) -> some View {
+        content
+            .listRowBackground(isAMOLED ? Color.black : nil)
+    }
+}
+
+extension View {
+    /// Apply to each row view inside a List or Form section to get a true-black background in AMOLED mode.
+    func themedRow() -> some View {
+        modifier(ThemedRowStyle())
+    }
+}
diff --git a/Hutch/Views/Auth/AuthView.swift b/Hutch/Views/Auth/AuthView.swift
index 5a249ef..d114981 100644
--- a/Hutch/Views/Auth/AuthView.swift
+++ b/Hutch/Views/Auth/AuthView.swift
@@ -15,6 +15,7 @@ struct TokenEntryView: View {
             Form {
                 Section {
                     Text("Enter your SourceHut personal access token to connect.")
+                        .themedRow()
                 } header: {
                     Text("Welcome to Hutch")
                 } footer: {
@@ -27,6 +28,7 @@ struct TokenEntryView: View {
                         .autocorrectionDisabled()
                         .textInputAutocapitalization(.never)
                         .disabled(isConnecting)
+                        .themedRow()
                 } header: {
                     Text("Token")
                 }
@@ -40,6 +42,7 @@ struct TokenEntryView: View {
                                 .foregroundStyle(.red)
                         }
                         .foregroundStyle(.red)
+                        .themedRow()
                     }
                 }
 
@@ -56,22 +59,26 @@ struct TokenEntryView: View {
                         }
                     }
                     .disabled(tokenTrimmed.isEmpty || isConnecting)
+                    .themedRow()
                 }
 
                 Section {
                     Link(destination: createAccountURL) {
                         Label("Create SourceHut account", systemImage: "person.badge.plus")
                     }
+                    .themedRow()
 
                     Link(destination: personalAccessTokensURL) {
                         Label("Create Personal Access Token", systemImage: "key")
                     }
+                    .themedRow()
                 } header: {
                     Text("Need an account?")
                 } footer: {
                     Text("These links open SourceHut in your browser. After signing up, create a Personal Access Token there and paste it here.")
                 }
             }
+            .themedList()
             .navigationTitle("Hutch")
         }
     }
diff --git a/Hutch/Views/Builds/BuildDetailView.swift b/Hutch/Views/Builds/BuildDetailView.swift
index 5895f48..46fb42d 100644
--- a/Hutch/Views/Builds/BuildDetailView.swift
+++ b/Hutch/Views/Builds/BuildDetailView.swift
@@ -176,26 +176,34 @@ struct BuildDetailView: View {
                                 .font(.subheadline.weight(.medium))
                         }
                     }
+                    .themedRow()
 
                     if let note = job.note, !note.isEmpty {
                         LabeledContent("Note", value: note)
+                            .themedRow()
                     }
 
                     if let image = job.image {
                         LabeledContent("Image", value: image)
+                            .themedRow()
                     }
 
                     if !job.tags.isEmpty {
                         LabeledContent("Tags", value: job.tags.joined(separator: ", "))
+                            .themedRow()
                     }
 
                     if let visibility = job.visibility {
                         LabeledContent("Visibility", value: visibility.rawValue.capitalized)
+                            .themedRow()
                     }
 
                     LabeledContent("Owner", value: job.owner.canonicalName)
+                        .themedRow()
                     LabeledContent("Created", value: job.created.relativeDescription)
+                        .themedRow()
                     LabeledContent("Updated", value: job.updated.relativeDescription)
+                        .themedRow()
                 }
 
                 if appState.isDebugModeEnabled {
@@ -211,9 +219,11 @@ struct BuildDetailView: View {
                             url: \(SRHTWebURL.build(jobId: job.id, ownerCanonicalName: job.owner.canonicalName)?.absoluteString ?? "unavailable")
                             """
                         )
+                        .themedRow()
 
                         if let rawJobResponse = viewModel.rawJobResponse {
                             DebugTextBlock(title: "Raw Response", content: rawJobResponse)
+                                .themedRow()
                         }
                     }
                 }
@@ -237,6 +247,7 @@ struct BuildDetailView: View {
                             }
                         }
                         .disabled(isOpeningRepository)
+                        .themedRow()
                     }
                 }
 
@@ -248,6 +259,7 @@ struct BuildDetailView: View {
                                 openURL(url)
                             }
                         }
+                        .themedRow()
                     } header: {
                         Text("Artifacts")
                     } footer: {
@@ -275,6 +287,7 @@ struct BuildDetailView: View {
                                 }
                             }
                             .foregroundStyle(.primary)
+                            .themedRow()
                         } header: {
                             HStack(spacing: 6) {
                                 TaskStatusIcon(status: task.status)
@@ -299,6 +312,7 @@ struct BuildDetailView: View {
                             }
                         }
                         .disabled(viewModel.isCancelling)
+                        .themedRow()
                     }
                 }
 
@@ -319,6 +333,7 @@ struct BuildDetailView: View {
                             }
                         }
                         .disabled(viewModel.isRebuilding)
+                        .themedRow()
 
                         Button {
                             showEditResubmitSheet = true
@@ -326,11 +341,13 @@ struct BuildDetailView: View {
                             Text("Edit & Resubmit")
                         }
                         .disabled(viewModel.isSubmittingEditedBuild)
+                        .themedRow()
                     } footer: {
                         Text("Creates a new build using this job’s saved manifest, tags, note, and visibility.")
                     }
                 }
             }
+            .themedList()
             .task(id: job.id) {
                 RecentActivityStore.recordBuild(
                     jobId: job.id,
@@ -463,26 +480,33 @@ private struct EditResubmitBuildSheet: View {
                         .lineLimit(12...24)
                         .textInputAutocapitalization(.never)
                         .autocorrectionDisabled()
+                        .themedRow()
                 }
 
                 Section("Build Options") {
                     TextField("Note (optional)", text: $note)
+                        .themedRow()
                     TextField("Tags (comma-separated, optional)", text: $tagsText)
                         .textInputAutocapitalization(.never)
                         .autocorrectionDisabled()
+                        .themedRow()
                     Picker("Visibility", selection: $visibility) {
                         Text("Public").tag(Visibility.public)
                         Text("Unlisted").tag(Visibility.unlisted)
                         Text("Private").tag(Visibility.private)
                     }
+                    .themedRow()
                     Toggle("Start build now", isOn: $execute)
+                        .themedRow()
                     Toggle("Allow build secrets", isOn: $secrets)
+                        .themedRow()
                 }
 
                 Section {
                     Text("This submits a new build. “Start build now” and “Allow build secrets” use local defaults because the current job does not include those original values.")
                         .font(.footnote)
                         .foregroundStyle(.secondary)
+                        .themedRow()
                 }
 
                 if let actionError = viewModel.actionError {
@@ -494,6 +518,7 @@ private struct EditResubmitBuildSheet: View {
                                 .foregroundStyle(.red)
                         }
                         .foregroundStyle(.red)
+                        .themedRow()
                     }
                 }
             }
diff --git a/Hutch/Views/Builds/BuildListView.swift b/Hutch/Views/Builds/BuildListView.swift
index f4647e4..fc91ea1 100644
--- a/Hutch/Views/Builds/BuildListView.swift
+++ b/Hutch/Views/Builds/BuildListView.swift
@@ -5,6 +5,7 @@ struct BuildListView: View {
     @AppStorage(AppStorageKeys.buildsAutoRefreshInterval) private var autoRefreshRawValue = 0
     @AppStorage(AppStorageKeys.buildsRepoFilter) private var savedRepoFilter = ""
     @Environment(AppState.self) private var appState
+    @Environment(\.isAMOLEDTheme) private var isAMOLED
     @State private var viewModel: BuildListViewModel?
     @State private var showSubmitSheet = false
     @State private var submittedJobId: Int?
@@ -134,7 +135,7 @@ struct BuildListView: View {
                 .padding(.top, 6)
                 .padding(.bottom, 10)
                 .listRowInsets(EdgeInsets())
-                .listRowBackground(Color.clear)
+                .listRowBackground(isAMOLED ? Color.black : Color.clear)
                 .listRowSeparator(.hidden)
 
                 ForEach(viewModel.filteredJobs) { job in
@@ -181,6 +182,7 @@ struct BuildListView: View {
                         await viewModel.loadMoreIfNeeded(currentItem: job)
                     }
                 }
+                .themedRow()
 
                 if viewModel.isLoadingMore {
                     HStack {
@@ -189,6 +191,7 @@ struct BuildListView: View {
                         Spacer()
                     }
                     .listRowSeparator(.hidden)
+                    .themedRow()
                 }
             }
         }
@@ -279,26 +282,33 @@ private struct SubmitBuildSheet: View {
                         .lineLimit(12...24)
                         .textInputAutocapitalization(.never)
                         .autocorrectionDisabled()
+                        .themedRow()
                 }
 
                 Section("Build Options") {
                     TextField("Note (optional)", text: $note)
+                        .themedRow()
                     TextField("Tags (comma-separated, optional)", text: $tagsText)
                         .textInputAutocapitalization(.never)
                         .autocorrectionDisabled()
+                        .themedRow()
                     Picker("Visibility", selection: $visibility) {
                         Text("Public").tag(Visibility.public)
                         Text("Unlisted").tag(Visibility.unlisted)
                         Text("Private").tag(Visibility.private)
                     }
+                    .themedRow()
                     Toggle("Start build now", isOn: $execute)
+                        .themedRow()
                     Toggle("Allow build secrets", isOn: $secrets)
+                        .themedRow()
                 }
 
                 Section {
                     Text("You need a valid builds.sr.ht manifest and a token with BUILDS:RW.")
                         .font(.footnote)
                         .foregroundStyle(.secondary)
+                        .themedRow()
                 }
 
                 if let error = viewModel.error {
@@ -310,9 +320,11 @@ private struct SubmitBuildSheet: View {
                                 .foregroundStyle(.red)
                         }
                         .foregroundStyle(.red)
+                        .themedRow()
                     }
                 }
             }
+            .themedList()
             .navigationTitle("Submit Build")
             .navigationBarTitleDisplayMode(.inline)
             .onDisappear {
diff --git a/Hutch/Views/Home/HomeView.swift b/Hutch/Views/Home/HomeView.swift
index 105b62b..b10926a 100644
--- a/Hutch/Views/Home/HomeView.swift
+++ b/Hutch/Views/Home/HomeView.swift
@@ -94,6 +94,7 @@ struct HomeView: View {
                     )
                 }
                 .buttonStyle(.plain)
+                .themedRow()
             }
         }
     }
@@ -109,6 +110,7 @@ struct HomeView: View {
                     emphasis: .action
                 )
             }
+            .themedRow()
         }
     }
 
@@ -126,6 +128,7 @@ struct HomeView: View {
                     .disabled(isOpeningRecentItem)
                     .listRowSeparator(.hidden)
                 }
+                .themedRow()
             }
         }
     }
@@ -143,6 +146,7 @@ struct HomeView: View {
                     emphasis: .monitoring
                 )
             }
+            .themedRow()
         }
     }
 
@@ -156,6 +160,7 @@ struct HomeView: View {
                 } label: {
                     HomeCompactMessageRow(text: "Pin projects for quick access", systemImage: "pin")
                 }
+                .themedRow()
             } else {
                 LazyVGrid(
                     columns: [
@@ -174,6 +179,7 @@ struct HomeView: View {
                     }
                 }
                 .padding(.vertical, 2)
+                .themedRow()
             }
         }
     }
diff --git a/Hutch/Views/Inbox/ThreadDetailView.swift b/Hutch/Views/Inbox/ThreadDetailView.swift
index bde8172..028964c 100644
--- a/Hutch/Views/Inbox/ThreadDetailView.swift
+++ b/Hutch/Views/Inbox/ThreadDetailView.swift
@@ -123,6 +123,7 @@ struct ThreadDetailView: View {
                             .foregroundStyle(.secondary)
                     }
                     .padding(.vertical, 4)
+                    .themedRow()
                 }
 
                 Section("Related") {
@@ -138,6 +139,7 @@ struct ThreadDetailView: View {
                     } label: {
                         Label(thread.listDisplayName, systemImage: "list.bullet")
                     }
+                    .themedRow()
 
                     if let repo = self.thread.repo {
                         Button {
@@ -157,6 +159,7 @@ struct ThreadDetailView: View {
                             }
                         }
                         .disabled(isOpeningRepository)
+                        .themedRow()
                     }
                 }
 
@@ -165,6 +168,7 @@ struct ThreadDetailView: View {
                         Text(partialWarning)
                             .font(.caption)
                             .foregroundStyle(.secondary)
+                            .themedRow()
                     }
                 }
 
@@ -184,8 +188,10 @@ struct ThreadDetailView: View {
                         }
                     )
                 }
+                .themedRow()
             }
         }
+        .themedList()
         .listStyle(.plain)
         .toolbar {
             ToolbarItem(placement: .topBarTrailing) {
diff --git a/Hutch/Views/Lists/MailingListListView.swift b/Hutch/Views/Lists/MailingListListView.swift
index eca7343..2fcc640 100644
--- a/Hutch/Views/Lists/MailingListListView.swift
+++ b/Hutch/Views/Lists/MailingListListView.swift
@@ -142,7 +142,9 @@ struct MailingListListView: View {
                     .padding(.vertical, 2)
                 }
             }
+            .themedRow()
         }
+        .themedList()
         .listStyle(.plain)
         .searchable(
             text: $vm.searchText,
diff --git a/Hutch/Views/Lookup/LookupView.swift b/Hutch/Views/Lookup/LookupView.swift
index e53dd33..f45082d 100644
--- a/Hutch/Views/Lookup/LookupView.swift
+++ b/Hutch/Views/Lookup/LookupView.swift
@@ -356,6 +356,7 @@ struct LookupView: View {
                     }
                 }
                 .pickerStyle(.menu)
+                .themedRow()
 
                 TextField(
                     vm.selectedType.inputLabel,
@@ -368,6 +369,7 @@ struct LookupView: View {
                 .onSubmit {
                     Task { await vm.lookup() }
                 }
+                .themedRow()
             }
 
             Section {
@@ -382,6 +384,7 @@ struct LookupView: View {
                         ProgressView()
                     }
                 }
+                .themedRow()
             }
 
             if !vm.history.isEmpty {
@@ -403,11 +406,13 @@ struct LookupView: View {
                         }
                         .disabled(vm.isLooking)
                     }
+                    .themedRow()
 
                     Button("Clear History", role: .destructive) {
                         vm.clearHistory()
                     }
                     .disabled(vm.isLooking)
+                    .themedRow()
                 }
             }
         }
diff --git a/Hutch/Views/Lookup/UserProfileView.swift b/Hutch/Views/Lookup/UserProfileView.swift
index 4fbb5a0..8472a90 100644
--- a/Hutch/Views/Lookup/UserProfileView.swift
+++ b/Hutch/Views/Lookup/UserProfileView.swift
@@ -2,6 +2,7 @@ import SwiftUI
 
 struct UserProfileView: View {
     @Environment(AppState.self) private var appState
+    @Environment(\.isAMOLEDTheme) private var isAMOLED
     @AppStorage(AppStorageKeys.contributionGraphsEnabled, store: .standard) private var contributionGraphsEnabled = true
 
     let user: User
@@ -55,33 +56,41 @@ struct UserProfileView: View {
                         }
                         Spacer()
                     }
-                    .listRowBackground(Color.clear)
+                    .listRowBackground(isAMOLED ? Color.black : Color.clear)
                 }
             }
 
             Section {
                 LabeledContent("Username", value: user.username)
+                    .themedRow()
                 LabeledContent("Canonical Name", value: user.canonicalName)
+                    .themedRow()
                 if let userType = user.userType {
                     LabeledContent("User Type", value: userType)
+                        .themedRow()
                 }
                 if let pronouns = user.pronouns {
                     LabeledContent("Pronouns", value: pronouns)
+                        .themedRow()
                 }
                 if let suspensionNotice = user.suspensionNotice {
                     LabeledContent("Suspension Notice", value: suspensionNotice)
+                        .themedRow()
                 }
             }
 
             Section {
                 LabeledContent("Email", value: user.email)
+                    .themedRow()
                 if let urlString = user.url, let url = URL(string: urlString) {
                     LabeledContent("URL") {
                         Link(urlString, destination: url)
                     }
+                    .themedRow()
                 }
                 if let location = user.location {
                     LabeledContent("Location", value: location)
+                        .themedRow()
                 }
             }
 
@@ -91,6 +100,7 @@ struct UserProfileView: View {
                         .frame(maxWidth: .infinity, alignment: .leading)
                         .tint(.accentColor)
                         .textSelection(.enabled)
+                        .themedRow()
                 }
             }
 
@@ -98,9 +108,11 @@ struct UserProfileView: View {
                 Section {
                     if let created = user.created {
                         LabeledContent("Joined", value: formattedTimestamp(created))
+                            .themedRow()
                     }
                     if let updated = user.updated {
                         LabeledContent("Updated", value: formattedTimestamp(updated))
+                            .themedRow()
                     }
                 }
             }
@@ -118,15 +130,18 @@ struct UserProfileView: View {
                             error: viewModel.contributionsError ?? viewModel.contributionStatusText,
                             isIndexedButEmpty: viewModel.isContributionActivityIndexedButEmpty
                         )
+                        .themedRow()
                     }
                 }
 
                 Section {
                     if viewModel.isLoadingRepositories && viewModel.repositories.isEmpty {
                         ProgressView()
+                            .themedRow()
                     } else if viewModel.repositories.isEmpty {
                         Text("No public repositories.")
                             .foregroundStyle(.secondary)
+                            .themedRow()
                     } else {
                         ForEach(viewModel.repositories.prefix(4)) { repo in
                             NavigationLink {
@@ -137,10 +152,12 @@ struct UserProfileView: View {
                                 RepositoryRowView(repository: repo, buildStatus: .none)
                             }
                         }
+                        .themedRow()
                         if viewModel.repositories.count > 4 {
                             NavigationLink("See All") {
                                 UserRepositoriesView(viewModel: viewModel)
                             }
+                            .themedRow()
                         }
                     }
                 } header: {
@@ -150,9 +167,11 @@ struct UserProfileView: View {
                 Section {
                     if viewModel.isLoadingTrackers && viewModel.trackers.isEmpty {
                         ProgressView()
+                            .themedRow()
                     } else if viewModel.trackers.isEmpty {
                         Text("No public trackers.")
                             .foregroundStyle(.secondary)
+                            .themedRow()
                     } else {
                         ForEach(viewModel.trackers.prefix(4)) { tracker in
                             NavigationLink {
@@ -161,10 +180,12 @@ struct UserProfileView: View {
                                 UserProfileTrackerRowView(tracker: tracker)
                             }
                         }
+                        .themedRow()
                         if viewModel.trackers.count > 4 {
                             NavigationLink("See All") {
                                 UserTrackersView(viewModel: viewModel)
                             }
+                            .themedRow()
                         }
                     }
                 } header: {
@@ -172,6 +193,7 @@ struct UserProfileView: View {
                 }
             }
         }
+        .themedList()
         .listStyle(.insetGrouped)
         .navigationTitle(user.canonicalName)
         .navigationBarTitleDisplayMode(.inline)
diff --git a/Hutch/Views/Lookup/UserRepositoriesView.swift b/Hutch/Views/Lookup/UserRepositoriesView.swift
index 7aeea10..c2ca65c 100644
--- a/Hutch/Views/Lookup/UserRepositoriesView.swift
+++ b/Hutch/Views/Lookup/UserRepositoriesView.swift
@@ -14,7 +14,9 @@ struct UserRepositoriesView: View {
                     RepositoryRowView(repository: repo, buildStatus: .none)
                 }
             }
+            .themedRow()
         }
+        .themedList()
         .listStyle(.plain)
         .navigationTitle("Repositories")
         .navigationBarTitleDisplayMode(.inline)
diff --git a/Hutch/Views/Lookup/UserTrackersView.swift b/Hutch/Views/Lookup/UserTrackersView.swift
index 17916da..c98d839 100644
--- a/Hutch/Views/Lookup/UserTrackersView.swift
+++ b/Hutch/Views/Lookup/UserTrackersView.swift
@@ -12,7 +12,9 @@ struct UserTrackersView: View {
                     UserProfileTrackerRowView(tracker: tracker)
                 }
             }
+            .themedRow()
         }
+        .themedList()
         .listStyle(.plain)
         .navigationTitle("Trackers")
         .navigationBarTitleDisplayMode(.inline)
diff --git a/Hutch/Views/More/AccountSwitcherView.swift b/Hutch/Views/More/AccountSwitcherView.swift
index 5fde9c5..ce69279 100644
--- a/Hutch/Views/More/AccountSwitcherView.swift
+++ b/Hutch/Views/More/AccountSwitcherView.swift
@@ -48,6 +48,7 @@ struct AccountSwitcherView: View {
                             pendingRemoval = appState.accounts[index]
                         }
                     }
+                    .themedRow()
                 }
 
                 Section {
@@ -57,8 +58,10 @@ struct AccountSwitcherView: View {
                         Label("Add Account", systemImage: "plus.circle")
                     }
                     .disabled(isSwitching)
+                    .themedRow()
                 }
             }
+            .themedList()
             .navigationTitle("Accounts")
             .navigationBarTitleDisplayMode(.inline)
             .toolbar {
diff --git a/Hutch/Views/More/AddAccountView.swift b/Hutch/Views/More/AddAccountView.swift
index cfaa2b2..c15db09 100644
--- a/Hutch/Views/More/AddAccountView.swift
+++ b/Hutch/Views/More/AddAccountView.swift
@@ -15,6 +15,7 @@ struct AddAccountView: View {
                     SecureField("Personal Access Token", text: $token)
                         .autocorrectionDisabled()
                         .textInputAutocapitalization(.never)
+                        .themedRow()
                 } footer: {
                     Text("Generate a token at meta.sr.ht → OAuth2 clients.")
                 }
@@ -23,9 +24,11 @@ struct AddAccountView: View {
                     Section {
                         Text(errorMessage)
                             .foregroundStyle(.red)
+                            .themedRow()
                     }
                 }
             }
+            .themedList()
             .navigationTitle("Add Account")
             .navigationBarTitleDisplayMode(.inline)
             .toolbar {
diff --git a/Hutch/Views/More/ManPageBrowserView.swift b/Hutch/Views/More/ManPageBrowserView.swift
index f854c79..c22eafe 100644
--- a/Hutch/Views/More/ManPageBrowserView.swift
+++ b/Hutch/Views/More/ManPageBrowserView.swift
@@ -25,8 +25,10 @@ struct ManPageBrowserView: View {
                         Text(doc.title)
                     }
                 }
+                .themedRow()
             }
         }
+        .themedList()
         .navigationTitle("Man Pages")
         .navigationBarTitleDisplayMode(.inline)
     }
diff --git a/Hutch/Views/More/MoreView.swift b/Hutch/Views/More/MoreView.swift
index 8be25ca..4666e53 100644
--- a/Hutch/Views/More/MoreView.swift
+++ b/Hutch/Views/More/MoreView.swift
@@ -16,24 +16,29 @@ struct MoreView: View {
                 NavigationLink(value: MoreRoute.lookup) {
                     Label("Look Up", systemImage: "magnifyingglass")
                 }
+                .themedRow()
             }
 
             Section("Other Services") {
                 NavigationLink(value: MoreRoute.projects) {
                     Label("Projects", systemImage: "square.stack.3d.up")
                 }
+                .themedRow()
 
                 NavigationLink(value: MoreRoute.lists) {
                     Label("Mailing Lists", systemImage: "list.bullet.rectangle")
                 }
+                .themedRow()
 
                 NavigationLink(value: MoreRoute.manPageBrowser) {
                     Label("Man Pages", systemImage: "book")
                 }
+                .themedRow()
 
                 NavigationLink(value: MoreRoute.pastes) {
                     Label("Pastes", systemImage: "doc.on.clipboard")
                 }
+                .themedRow()
 
                 NavigationLink(value: MoreRoute.systemStatus) {
                     SystemStatusSummaryRow(
@@ -43,16 +48,19 @@ struct MoreView: View {
                         isShowingStaleData: viewModel?.isShowingStaleSystemStatus ?? false
                     )
                 }
+                .themedRow()
             }
 
             Section("Meta") {
                 NavigationLink(value: MoreRoute.profile) {
                     Label("Profile", systemImage: "person.text.rectangle")
                 }
+                .themedRow()
 
                 NavigationLink(value: MoreRoute.settings) {
                     Label("Settings", systemImage: "gear")
                 }
+                .themedRow()
             }
 
             Section {
@@ -61,6 +69,7 @@ struct MoreView: View {
                         Label(item.title, systemImage: "safari")
                     }
                 }
+                .themedRow()
             } header: {
                 Text("External Links")
             } footer: {
diff --git a/Hutch/Views/More/ProfileView.swift b/Hutch/Views/More/ProfileView.swift
index db3cb53..c53a89d 100644
--- a/Hutch/Views/More/ProfileView.swift
+++ b/Hutch/Views/More/ProfileView.swift
@@ -79,6 +79,7 @@ struct ProfileView: View {
                             error: contributionViewModel.contributionsError ?? contributionViewModel.contributionStatusText,
                             isIndexedButEmpty: contributionViewModel.isContributionActivityIndexedButEmpty
                         )
+                        .themedRow()
                     }
                 }
 
@@ -185,6 +186,7 @@ struct ProfileView: View {
                 }
             }
             .padding(.vertical, 4)
+            .themedRow()
 
             if let bio = profile.bio, !bio.isEmpty {
                 VStack(alignment: .leading, spacing: 2) {
@@ -193,36 +195,44 @@ struct ProfileView: View {
                         .foregroundStyle(.secondary)
                     ProfileBioView(markdown: bio)
                 }
+                .themedRow()
             }
 
             if let location = profile.location, !location.isEmpty {
                 LabeledContent("Location", value: location)
+                    .themedRow()
             }
 
             if let url = profile.url, !url.isEmpty {
                 LabeledContent("URL", value: url)
+                    .themedRow()
             }
 
             if let status = profile.paymentStatus {
                 LabeledContent("Payment", value: status.capitalized)
+                    .themedRow()
             }
 
             if let sub = profile.subscription {
                 if let status = sub.status {
                     LabeledContent("Subscription", value: status.capitalized)
+                        .themedRow()
                 }
                 if let interval = sub.interval {
                     LabeledContent("Interval", value: interval.capitalized)
+                        .themedRow()
                 }
             }
 
             Button("Edit Profile") {
                 viewModel.isEditingProfile = true
             }
+            .themedRow()
 
             SRHTShareButton(url: SRHTWebURL.profile(canonicalName: profile.canonicalName), target: .profile) {
                 SwiftUI.Label("Share Profile", systemImage: "square.and.arrow.up")
             }
+            .themedRow()
         }
     }
 
@@ -262,11 +272,13 @@ struct ProfileView: View {
                     }
                 }
             }
+            .themedRow()
 
             if viewModel.isAddingSSHKey {
                 TextField("Paste SSH public key", text: $vm.newSSHKey, axis: .vertical)
                     .font(.caption.monospaced())
                     .lineLimit(3...6)
+                    .themedRow()
 
                 HStack {
                     Button("Cancel") {
@@ -280,12 +292,14 @@ struct ProfileView: View {
                     .buttonStyle(.borderedProminent)
                     .disabled(viewModel.newSSHKey.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
                 }
+                .themedRow()
             } else {
                 Button {
                     viewModel.isAddingSSHKey = true
                 } label: {
                     SwiftUI.Label("Add SSH Key", systemImage: "key")
                 }
+                .themedRow()
             }
         } header: {
             Text("SSH Keys")
@@ -316,11 +330,13 @@ struct ProfileView: View {
                     }
                 }
             }
+            .themedRow()
 
             if viewModel.isAddingPGPKey {
                 TextField("Paste PGP public key", text: $vm.newPGPKey, axis: .vertical)
                     .font(.caption.monospaced())
                     .lineLimit(3...6)
+                    .themedRow()
 
                 HStack {
                     Button("Cancel") {
@@ -334,12 +350,14 @@ struct ProfileView: View {
                     .buttonStyle(.borderedProminent)
                     .disabled(viewModel.newPGPKey.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
                 }
+                .themedRow()
             } else {
                 Button {
                     viewModel.isAddingPGPKey = true
                 } label: {
                     SwiftUI.Label("Add PGP Key", systemImage: "key.fill")
                 }
+                .themedRow()
             }
         } header: {
             Text("PGP Keys")
@@ -357,10 +375,12 @@ struct ProfileView: View {
                     ProgressView()
                     Spacer()
                 }
+                .themedRow()
             } else if viewModel.personalAccessTokens.isEmpty {
                 Button("Load Tokens") {
                     Task { await viewModel.loadPersonalAccessTokens() }
                 }
+                .themedRow()
             } else {
                 ForEach(viewModel.personalAccessTokens) { token in
                     VStack(alignment: .leading, spacing: 4) {
@@ -390,6 +410,7 @@ struct ProfileView: View {
                         }
                     }
                 }
+                .themedRow()
             }
         } header: {
             Text("Personal Access Tokens")
@@ -426,6 +447,7 @@ private struct EditProfileSheet: View {
     let profile: UserProfile
     let viewModel: SettingsViewModel
 
+    @Environment(\.isAMOLEDTheme) private var isAMOLED
     @State private var email: String
     @State private var url: String
     @State private var location: String
@@ -492,7 +514,7 @@ private struct EditProfileSheet: View {
                         }
                         Spacer()
                     }
-                    .listRowBackground(Color.clear)
+                    .listRowBackground(isAMOLED ? Color.black : Color.clear)
 
                     if profile.avatar != nil || avatarPreview != nil {
                         HStack {
@@ -506,7 +528,7 @@ private struct EditProfileSheet: View {
                             .disabled(viewModel.isUploadingAvatar)
                             Spacer()
                         }
-                        .listRowBackground(Color.clear)
+                        .listRowBackground(isAMOLED ? Color.black : Color.clear)
                         .listRowSeparator(.hidden)
                     }
                 }
@@ -522,6 +544,7 @@ private struct EditProfileSheet: View {
                             .autocorrectionDisabled()
                             .textInputAutocapitalization(.never)
                     }
+                    .themedRow()
 
                     VStack(alignment: .leading, spacing: 4) {
                         Text("URL")
@@ -533,6 +556,7 @@ private struct EditProfileSheet: View {
                             .autocorrectionDisabled()
                             .textInputAutocapitalization(.never)
                     }
+                    .themedRow()
 
                     VStack(alignment: .leading, spacing: 4) {
                         Text("Location")
@@ -540,6 +564,7 @@ private struct EditProfileSheet: View {
                             .foregroundStyle(.secondary)
                         TextField("Enter location", text: $location)
                     }
+                    .themedRow()
 
                     VStack(alignment: .leading, spacing: 4) {
                         Text("Bio")
@@ -548,8 +573,10 @@ private struct EditProfileSheet: View {
                         TextField("Enter bio", text: $bio, axis: .vertical)
                             .lineLimit(3...6)
                     }
+                    .themedRow()
                 }
             }
+            .themedList()
             .navigationTitle("Edit Profile")
             .navigationBarTitleDisplayMode(.inline)
             .onChange(of: selectedPhoto) { _, newItem in
diff --git a/Hutch/Views/Pastes/PasteDetailView.swift b/Hutch/Views/Pastes/PasteDetailView.swift
index 7eb1853..c6fe11a 100644
--- a/Hutch/Views/Pastes/PasteDetailView.swift
+++ b/Hutch/Views/Pastes/PasteDetailView.swift
@@ -326,13 +326,18 @@ private struct PasteInfoSheet: View {
             List {
                 Section("Paste") {
                     LabeledContent("ID", value: paste.id)
+                        .themedRow()
                     LabeledContent("Owner", value: paste.user.canonicalName)
+                        .themedRow()
                     LabeledContent("Created", value: paste.created.relativeDescription)
+                        .themedRow()
                     LabeledContent("Visibility") {
                         VisibilityBadge(visibility: paste.visibility)
                     }
+                    .themedRow()
                     if paste.files.count > 1 {
                         LabeledContent("Files", value: "\(paste.files.count)")
+                            .themedRow()
                     }
                 }
 
@@ -340,6 +345,7 @@ private struct PasteInfoSheet: View {
                     Section("File") {
                         if let filename = file.filename, !filename.isEmpty {
                             LabeledContent("Filename", value: filename)
+                                .themedRow()
                         }
                         LabeledContent("Hash") {
                             Text(file.hash)
@@ -347,6 +353,7 @@ private struct PasteInfoSheet: View {
                                 .foregroundStyle(.secondary)
                                 .textSelection(.enabled)
                         }
+                        .themedRow()
                     }
                 }
             }
@@ -407,6 +414,7 @@ private struct PasteVisibilitySheet: View {
                     }
                     .buttonStyle(.plain)
                 }
+                .themedRow()
             }
             .listStyle(.insetGrouped)
             .navigationTitle("Visibility")
diff --git a/Hutch/Views/Pastes/PasteListView.swift b/Hutch/Views/Pastes/PasteListView.swift
index 9363b08..dfd472b 100644
--- a/Hutch/Views/Pastes/PasteListView.swift
+++ b/Hutch/Views/Pastes/PasteListView.swift
@@ -101,6 +101,7 @@ struct PasteListView: View {
                     await viewModel.loadMoreIfNeeded(currentItem: paste)
                 }
             }
+            .themedRow()
 
             if viewModel.isLoadingMore {
                 HStack {
@@ -109,6 +110,7 @@ struct PasteListView: View {
                     Spacer()
                 }
                 .listRowSeparator(.hidden)
+                .themedRow()
             }
         }
         .themedList()
@@ -302,12 +304,14 @@ private struct CreatePasteSheet: View {
                             files = [PasteUploadDraft()]
                         }
                     }
+                    .themedRow()
 
                     Button {
                         files.append(PasteUploadDraft())
                     } label: {
                         Label("Add File", systemImage: "plus")
                     }
+                    .themedRow()
                 }
 
                 Section("Visibility") {
@@ -316,21 +320,25 @@ private struct CreatePasteSheet: View {
                         Text("Unlisted").tag(Visibility.unlisted)
                         Text("Private").tag(Visibility.private)
                     }
+                    .themedRow()
                 }
 
                 Section {
                     Text("Paste contents are uploaded as UTF-8 text files. Hutch can change visibility later, but the API does not support editing file contents after creation.")
                         .font(.footnote)
                         .foregroundStyle(.secondary)
+                        .themedRow()
                 }
 
                 if let error = viewModel.error {
                     Section {
                         Label(error, systemImage: "exclamationmark.triangle.fill")
                             .foregroundStyle(.red)
+                            .themedRow()
                     }
                 }
             }
+            .themedList()
             .navigationTitle("New Paste")
             .navigationBarTitleDisplayMode(.inline)
             .toolbar {
diff --git a/Hutch/Views/Projects/ProjectDetailView.swift b/Hutch/Views/Projects/ProjectDetailView.swift
index dc8b24d..b5c6cdb 100644
--- a/Hutch/Views/Projects/ProjectDetailView.swift
+++ b/Hutch/Views/Projects/ProjectDetailView.swift
@@ -103,6 +103,7 @@ struct ProjectDetailView: View {
                 }
             }
             .padding(.vertical, 4)
+            .themedRow()
         }
     }
 
@@ -127,6 +128,7 @@ struct ProjectDetailView: View {
                     }
                     .buttonStyle(.plain)
                 }
+                .themedRow()
             }
         }
     }
@@ -155,6 +157,7 @@ struct ProjectDetailView: View {
                     }
                     .buttonStyle(.plain)
                 }
+                .themedRow()
             }
         }
     }
@@ -183,6 +186,7 @@ struct ProjectDetailView: View {
                     }
                     .buttonStyle(.plain)
                 }
+                .themedRow()
             }
         }
     }
@@ -205,6 +209,7 @@ struct ProjectDetailView: View {
                     }
                     .buttonStyle(.plain)
                 }
+                .themedRow()
             }
         }
     }
@@ -218,6 +223,7 @@ struct ProjectDetailView: View {
                     systemImage: "square.stack.3d.up.slash",
                     description: Text("This project doesn’t currently expose repositories, trackers, mailing lists, or external links.")
                 )
+                .themedRow()
             }
         }
     }
diff --git a/Hutch/Views/Projects/ProjectMailingListView.swift b/Hutch/Views/Projects/ProjectMailingListView.swift
index 4ff93e9..d7c0abc 100644
--- a/Hutch/Views/Projects/ProjectMailingListView.swift
+++ b/Hutch/Views/Projects/ProjectMailingListView.swift
@@ -332,7 +332,9 @@ struct MailingListDetailView: View {
                     .tint(thread.isUnread ? .blue : .gray)
                 }
             }
+            .themedRow()
         }
+        .themedList()
         .listStyle(.plain)
         .searchable(
             text: $vm.searchText,
diff --git a/Hutch/Views/Projects/ProjectsListView.swift b/Hutch/Views/Projects/ProjectsListView.swift
index 5786462..12a5149 100644
--- a/Hutch/Views/Projects/ProjectsListView.swift
+++ b/Hutch/Views/Projects/ProjectsListView.swift
@@ -79,6 +79,7 @@ struct ProjectsListView: View {
                 .buttonStyle(.plain)
                 .alignmentGuide(.listRowSeparatorLeading) { _ in 0 }
             }
+            .themedRow()
         }
         .themedList()
         .listStyle(.plain)
diff --git a/Hutch/Views/Repositories/ArtifactsView.swift b/Hutch/Views/Repositories/ArtifactsView.swift
index ef3b972..b8caf4c 100644
--- a/Hutch/Views/Repositories/ArtifactsView.swift
+++ b/Hutch/Views/Repositories/ArtifactsView.swift
@@ -13,9 +13,11 @@ struct ArtifactsView: View {
                             openURL(artifact.url)
                         }
                     }
+                    .themedRow()
                 }
             }
         }
+        .themedList()
         .listStyle(.insetGrouped)
         .overlay {
             if viewModel.isLoadingArtifacts, viewModel.referenceArtifacts.isEmpty {
diff --git a/Hutch/Views/Repositories/CommitLogView.swift b/Hutch/Views/Repositories/CommitLogView.swift
index bc8ccc9..85e4d7a 100644
--- a/Hutch/Views/Repositories/CommitLogView.swift
+++ b/Hutch/Views/Repositories/CommitLogView.swift
@@ -13,6 +13,7 @@ struct CommitLogView: View {
                     await viewModel.loadMoreCommitsIfNeeded(currentItem: commit)
                 }
             }
+            .themedRow()
 
             if viewModel.isLoadingMoreCommits {
                 HStack {
@@ -21,8 +22,10 @@ struct CommitLogView: View {
                     Spacer()
                 }
                 .listRowSeparator(.hidden)
+                .themedRow()
             }
         }
+        .themedList()
         .listStyle(.plain)
         .overlay {
             if viewModel.isLoadingCommits, viewModel.commits.isEmpty {
diff --git a/Hutch/Views/Repositories/FileTreeView.swift b/Hutch/Views/Repositories/FileTreeView.swift
index ec33822..b49f361 100644
--- a/Hutch/Views/Repositories/FileTreeView.swift
+++ b/Hutch/Views/Repositories/FileTreeView.swift
@@ -233,7 +233,9 @@ private struct FileTreeContentView: View {
                         await viewModel.navigateInto(entry: entry)
                     }
                 }
+                .themedRow()
         }
+        .themedList()
         .listStyle(.plain)
         .refreshable {
             await viewModel.loadRootTree()
@@ -848,6 +850,7 @@ private struct RefPickerSheet: View {
                         )
                     }
                     .buttonStyle(.plain)
+                    .themedRow()
                 }
 
                 if !viewModel.branches.isEmpty {
@@ -868,6 +871,7 @@ private struct RefPickerSheet: View {
                             }
                             .buttonStyle(.plain)
                         }
+                        .themedRow()
                     }
                 }
 
@@ -889,9 +893,11 @@ private struct RefPickerSheet: View {
                             }
                             .buttonStyle(.plain)
                         }
+                        .themedRow()
                     }
                 }
             }
+            .themedList()
             .listStyle(.insetGrouped)
             .navigationTitle("Select Ref")
             .navigationBarTitleDisplayMode(.inline)
diff --git a/Hutch/Views/Repositories/HgRepositoryDetailView.swift b/Hutch/Views/Repositories/HgRepositoryDetailView.swift
index 8362467..047a495 100644
--- a/Hutch/Views/Repositories/HgRepositoryDetailView.swift
+++ b/Hutch/Views/Repositories/HgRepositoryDetailView.swift
@@ -396,7 +396,9 @@ struct HgRepositoryDetailView: View {
                     .onTapGesture {
                         Task { await viewModel.openFile(file) }
                     }
+                    .themedRow()
                 }
+                .themedList()
                 .listStyle(.plain)
             }
         }
@@ -454,6 +456,7 @@ struct HgRepositoryDetailView: View {
                         await viewModel.loadMoreLogIfNeeded(currentItem: revision)
                     }
             }
+            .themedRow()
 
             if viewModel.isLoadingMoreLog {
                 HStack {
@@ -462,8 +465,10 @@ struct HgRepositoryDetailView: View {
                     Spacer()
                 }
                 .listRowSeparator(.hidden)
+                .themedRow()
             }
         }
+        .themedList()
         .listStyle(.plain)
         .overlay {
             if viewModel.isLoadingLog, viewModel.log.isEmpty {
@@ -498,7 +503,9 @@ struct HgRepositoryDetailView: View {
         } else {
             List(revisions) { revision in
                 namedRevisionRow(revision)
+                    .themedRow()
             }
+            .themedList()
             .listStyle(.plain)
         }
     }
@@ -699,6 +706,7 @@ private struct HgBrowseRefPickerSheet: View {
                         )
                     }
                     .buttonStyle(.plain)
+                    .themedRow()
                 }
 
                 if !viewModel.branches.isEmpty {
@@ -719,6 +727,7 @@ private struct HgBrowseRefPickerSheet: View {
                             }
                             .buttonStyle(.plain)
                         }
+                        .themedRow()
                     }
                 }
 
@@ -740,6 +749,7 @@ private struct HgBrowseRefPickerSheet: View {
                             }
                             .buttonStyle(.plain)
                         }
+                        .themedRow()
                     }
                 }
 
@@ -761,9 +771,11 @@ private struct HgBrowseRefPickerSheet: View {
                             }
                             .buttonStyle(.plain)
                         }
+                        .themedRow()
                     }
                 }
             }
+            .themedList()
             .listStyle(.insetGrouped)
             .navigationTitle("Select Ref")
             .navigationBarTitleDisplayMode(.inline)
diff --git a/Hutch/Views/Repositories/HgRepositorySettingsView.swift b/Hutch/Views/Repositories/HgRepositorySettingsView.swift
index a6aa728..e3c97e0 100644
--- a/Hutch/Views/Repositories/HgRepositorySettingsView.swift
+++ b/Hutch/Views/Repositories/HgRepositorySettingsView.swift
@@ -49,6 +49,7 @@ struct HgRepositorySettingsView: View {
             histeditSection(viewModel)
             deleteSection(viewModel)
         }
+        .themedList()
         .srhtErrorBanner(error: $vm.error)
         .alert(
             "Permanently delete \(repository.owner.canonicalName)/\(repository.name)?",
@@ -101,25 +102,30 @@ struct HgRepositorySettingsView: View {
                 Text("\(repository.owner.canonicalName)/\(repository.name)")
                     .font(.body.monospaced())
             }
+            .themedRow()
 
             LabeledContent("Forge") {
                 Text(repositoryForgeLabel(repository.service))
             }
+            .themedRow()
 
             LabeledContent("Visibility") {
                 Text(repositoryVisibilityLabel(viewModel.editedVisibility))
             }
+            .themedRow()
         }
 
         Section("Repository Details") {
             TextField("Description", text: Bindable(viewModel).editedDescription, axis: .vertical)
                 .lineLimit(3...6)
+                .themedRow()
 
             Picker("Visibility", selection: Bindable(viewModel).editedVisibility) {
                 Text("Public").tag(Visibility.public)
                 Text("Unlisted").tag(Visibility.unlisted)
                 Text("Private").tag(Visibility.private)
             }
+            .themedRow()
 
             Button {
                 Task {
@@ -135,6 +141,7 @@ struct HgRepositorySettingsView: View {
                 }
             }
             .disabled(viewModel.isSavingInfo || !viewModel.isInfoDirty)
+            .themedRow()
         }
     }
 
@@ -147,9 +154,11 @@ struct HgRepositorySettingsView: View {
                     ProgressView()
                     Spacer()
                 }
+                .themedRow()
             } else if viewModel.acls.isEmpty {
                 Text("No access entries yet.")
                     .foregroundStyle(.secondary)
+                    .themedRow()
             } else {
                 ForEach(viewModel.acls) { entry in
                     HStack {
@@ -167,6 +176,7 @@ struct HgRepositorySettingsView: View {
                         }
                     }
                 }
+                .themedRow()
             }
 
             HStack {
@@ -192,9 +202,11 @@ struct HgRepositorySettingsView: View {
                 }
                 .disabled(viewModel.isAddingACL || viewModel.newACLEntity.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
             }
+            .themedRow()
             Text("Add a SourceHut user and choose read-only or read/write access.")
                 .font(.caption)
                 .foregroundStyle(.secondary)
+                .themedRow()
         }
     }
 
@@ -202,9 +214,11 @@ struct HgRepositorySettingsView: View {
     private func featuresSection(_ viewModel: HgRepositorySettingsViewModel) -> some View {
         Section("Sensitive Settings") {
             Toggle("Hide this repository from public listings", isOn: Bindable(viewModel).editedNonPublishing)
+                .themedRow()
             Text("Changes stay pending until you save this section.")
                 .font(.caption)
                 .foregroundStyle(.secondary)
+                .themedRow()
 
             Button {
                 Task {
@@ -220,6 +234,7 @@ struct HgRepositorySettingsView: View {
                 }
             }
             .disabled(viewModel.isSavingInfo || !viewModel.isInfoDirty)
+            .themedRow()
         }
     }
 
@@ -230,16 +245,19 @@ struct HgRepositorySettingsView: View {
                 .autocorrectionDisabled()
                 .textInputAutocapitalization(.never)
                 .disabled(true)
+                .themedRow()
 
             Text("Removing revisions is not available through the public hg.sr.ht API, so Hutch can’t do this yet.")
                 .font(.caption)
                 .foregroundStyle(.secondary)
+                .themedRow()
 
             Button("Remove Revision", role: .destructive) {
                 // Not implemented: the hg.sr.ht API does not expose a histedit endpoint.
                 // This button is disabled until the API supports revision removal.
             }
                 .disabled(true)
+                .themedRow()
         }
     }
 
@@ -258,6 +276,7 @@ struct HgRepositorySettingsView: View {
                 }
             }
             .disabled(viewModel.isDeleting)
+            .themedRow()
         } header: {
             Text("Danger Zone")
         }
diff --git a/Hutch/Views/Repositories/ReferencesListView.swift b/Hutch/Views/Repositories/ReferencesListView.swift
index 6470a42..0e3be0a 100644
--- a/Hutch/Views/Repositories/ReferencesListView.swift
+++ b/Hutch/Views/Repositories/ReferencesListView.swift
@@ -10,6 +10,7 @@ struct ReferencesListView: View {
                     ForEach(viewModel.branches, id: \.name) { ref in
                         ReferenceRow(reference: ref, prefix: "refs/heads/")
                     }
+                    .themedRow()
                 }
             }
 
@@ -18,9 +19,11 @@ struct ReferencesListView: View {
                     ForEach(viewModel.tags, id: \.name) { ref in
                         ReferenceRow(reference: ref, prefix: "refs/tags/")
                     }
+                    .themedRow()
                 }
             }
         }
+        .themedList()
         .listStyle(.insetGrouped)
         .overlay {
             if viewModel.isLoadingRefs, viewModel.branches.isEmpty, viewModel.tags.isEmpty {
diff --git a/Hutch/Views/Repositories/RepositoryACLView.swift b/Hutch/Views/Repositories/RepositoryACLView.swift
index 0368b4f..29ded84 100644
--- a/Hutch/Views/Repositories/RepositoryACLView.swift
+++ b/Hutch/Views/Repositories/RepositoryACLView.swift
@@ -6,6 +6,7 @@ struct RepositoryACLView: View {
     let showsDoneButton: Bool
 
     @Environment(\.dismiss) private var dismiss
+    @Environment(\.isAMOLEDTheme) private var isAMOLED
     @State private var viewModel: RepositoryACLViewModel?
     @State private var pendingDeletion: RepositoryACLEntry?
     @State private var showAddSheet = false
@@ -70,7 +71,7 @@ struct RepositoryACLView: View {
                             Text("Only the repository owner currently has access.")
                         }
                         .frame(maxWidth: .infinity)
-                        .listRowBackground(Color.clear)
+                        .listRowBackground(isAMOLED ? Color.black : Color.clear)
                     } else {
                         Section {
                             ForEach(viewModel.visibleEntries) { entry in
@@ -86,9 +87,11 @@ struct RepositoryACLView: View {
                                     }
                                 )
                             }
+                            .themedRow()
                         }
                     }
                 }
+                .themedList()
                 .listStyle(.insetGrouped)
                 .refreshable {
                     await viewModel.load()
@@ -192,11 +195,13 @@ private struct RepositoryACLAddUserView: View {
                 TextField("Username or ~username", text: $viewModel.addUsername)
                     .autocorrectionDisabled()
                     .textInputAutocapitalization(.never)
+                    .themedRow()
 
                 if let validation = inlineValidationMessage {
                     Text(validation)
                         .font(.caption)
                         .foregroundStyle(.secondary)
+                        .themedRow()
                 }
             }
 
@@ -207,8 +212,10 @@ private struct RepositoryACLAddUserView: View {
                     }
                 }
                 .pickerStyle(.segmented)
+                .themedRow()
             }
         }
+        .themedList()
         .navigationTitle("Add User")
         .navigationBarTitleDisplayMode(.inline)
         .toolbar {
diff --git a/Hutch/Views/Repositories/RepositoryListView.swift b/Hutch/Views/Repositories/RepositoryListView.swift
index 0258a58..08ebb5c 100644
--- a/Hutch/Views/Repositories/RepositoryListView.swift
+++ b/Hutch/Views/Repositories/RepositoryListView.swift
@@ -78,6 +78,7 @@ struct RepositoryListView: View {
                         .foregroundStyle(.secondary)
                 }
                 .listRowSeparator(.hidden)
+                .themedRow()
             }
 
             ForEach(viewModel.repositories) { repo in
@@ -89,6 +90,7 @@ struct RepositoryListView: View {
                 }
                 .alignmentGuide(.listRowSeparatorLeading) { _ in 0 }
             }
+            .themedRow()
 
             if viewModel.isLoadingMore {
                 HStack {
@@ -97,6 +99,7 @@ struct RepositoryListView: View {
                     Spacer()
                 }
                 .listRowSeparator(.hidden)
+                .themedRow()
             }
         }
         .themedList()
@@ -223,16 +226,20 @@ private struct CreateRepositorySheet: View {
                             Text(service.displayName).tag(service)
                         }
                     }
+                    .themedRow()
                     TextField("Repository name", text: $name)
                         .textInputAutocapitalization(.never)
                         .autocorrectionDisabled()
+                        .themedRow()
                     TextField("Short description (optional)", text: $description, axis: .vertical)
                         .lineLimit(2...4)
+                        .themedRow()
                     Picker("Visibility", selection: $visibility) {
                         Text("Public").tag(Visibility.public)
                         Text("Unlisted").tag(Visibility.unlisted)
                         Text("Private").tag(Visibility.private)
                     }
+                    .themedRow()
                 }
 
                 Section("Import Existing Repository") {
@@ -241,16 +248,20 @@ private struct CreateRepositorySheet: View {
                             .textInputAutocapitalization(.never)
                             .autocorrectionDisabled()
                             .keyboardType(.URL)
+                            .themedRow()
                         Text("Import an existing Git repository from a remote URL.")
                             .font(.footnote)
                             .foregroundStyle(.secondary)
+                            .themedRow()
                     } else {
                         Text("Importing a Mercurial repository from a remote URL is not available through the public API.")
                             .font(.footnote)
                             .foregroundStyle(.secondary)
+                            .themedRow()
                     }
                 }
             }
+            .themedList()
             .navigationTitle(service == .git ? "New Git Repository" : "New Mercurial Repository")
             .navigationBarTitleDisplayMode(.inline)
             .toolbar {
diff --git a/Hutch/Views/Repositories/RepositorySettingsView.swift b/Hutch/Views/Repositories/RepositorySettingsView.swift
index 4736619..44b50ae 100644
--- a/Hutch/Views/Repositories/RepositorySettingsView.swift
+++ b/Hutch/Views/Repositories/RepositorySettingsView.swift
@@ -51,6 +51,7 @@ struct RepositorySettingsView: View {
             visibilitySection(viewModel)
             deleteSection(viewModel)
         }
+        .themedList()
         .srhtErrorBanner(error: $vm.error)
         .alert(
             visibilityConfirmationTitle(for: viewModel),
@@ -97,15 +98,18 @@ struct RepositorySettingsView: View {
                 Text("\(viewModel.repository.owner.canonicalName)/\(viewModel.repository.name)")
                     .font(.body.monospaced())
             }
+            .themedRow()
 
             LabeledContent("Default Branch") {
                 Text(viewModel.currentDefaultBranchName)
                     .font(.body.monospaced())
             }
+            .themedRow()
 
             LabeledContent("Visibility") {
                 Text(repositoryVisibilityLabel(viewModel.repository.visibility))
             }
+            .themedRow()
         }
     }
 
@@ -115,22 +119,27 @@ struct RepositorySettingsView: View {
             TextField("Repository name", text: Bindable(viewModel).editedName)
                 .autocorrectionDisabled()
                 .textInputAutocapitalization(.never)
+                .themedRow()
 
             TextField("Description", text: Bindable(viewModel).editedDescription, axis: .vertical)
                 .lineLimit(2...4)
+                .themedRow()
 
             if let metadataValidationMessage = viewModel.metadataValidationMessage {
                 Text(metadataValidationMessage)
                     .font(.caption)
                     .foregroundStyle(.red)
+                    .themedRow()
             } else if viewModel.normalizedEditedName != viewModel.repository.name {
                 Text("Changing the repository name updates the repository URL.")
                     .font(.caption)
                     .foregroundStyle(.secondary)
+                    .themedRow()
             } else {
                 Text("Name and description stay pending until you save this section.")
                     .font(.caption)
                     .foregroundStyle(.secondary)
+                    .themedRow()
             }
 
             Button {
@@ -153,6 +162,7 @@ struct RepositorySettingsView: View {
                 !viewModel.isMetadataDirty ||
                 viewModel.metadataValidationMessage != nil
             )
+            .themedRow()
         } header: {
             Text("Repository Details")
         }
@@ -165,10 +175,12 @@ struct RepositorySettingsView: View {
                 Text(viewModel.currentDefaultBranchName)
                     .font(.body.monospaced())
             }
+            .themedRow()
 
             if viewModel.branches.isEmpty {
                 Text("This repository doesn't have any branches yet.")
                     .foregroundStyle(.secondary)
+                    .themedRow()
             } else {
                 Picker("Branch", selection: Bindable(viewModel).editedHead) {
                     ForEach(viewModel.availableBranchNames, id: \.self) { branch in
@@ -177,10 +189,12 @@ struct RepositorySettingsView: View {
                             .tag(branch)
                     }
                 }
+                .themedRow()
 
                 Text("Changes stay pending until you set the new default branch.")
                     .font(.caption)
                     .foregroundStyle(.secondary)
+                    .themedRow()
 
                 Button {
                     Task {
@@ -202,6 +216,7 @@ struct RepositorySettingsView: View {
                     !viewModel.isDefaultBranchDirty ||
                     viewModel.defaultBranchValidationMessage != nil
                 )
+                .themedRow()
             }
         } header: {
             Text("Default Branch")
@@ -216,10 +231,12 @@ struct RepositorySettingsView: View {
                 Text("Unlisted").tag(Visibility.unlisted)
                 Text("Private").tag(Visibility.private)
             }
+            .themedRow()
 
             Text("Visibility changes apply immediately after you confirm them.")
                 .font(.caption)
                 .foregroundStyle(.secondary)
+                .themedRow()
 
             Button {
                 showVisibilityConfirmation = true
@@ -233,6 +250,7 @@ struct RepositorySettingsView: View {
                 }
             }
             .disabled(viewModel.isMutating || !viewModel.isVisibilityDirty)
+            .themedRow()
         } header: {
             Text("Sensitive Settings")
         }
@@ -253,6 +271,7 @@ struct RepositorySettingsView: View {
                 }
             }
             .disabled(viewModel.isMutating)
+            .themedRow()
         } header: {
             Text("Danger Zone")
         }
diff --git a/Hutch/Views/Settings/SettingsView.swift b/Hutch/Views/Settings/SettingsView.swift
index 44b3ba5..4ed9216 100644
--- a/Hutch/Views/Settings/SettingsView.swift
+++ b/Hutch/Views/Settings/SettingsView.swift
@@ -60,11 +60,13 @@ struct SettingsView: View {
                     Text(theme.label).tag(theme)
                 }
             }
+            .themedRow()
             Picker("Density", selection: $displayDensity) {
                 ForEach(DisplayDensity.allCases) { density in
                     Text(density.label).tag(density)
                 }
             }
+            .themedRow()
         } header: {
             Text("Appearance")
         } footer: {
@@ -76,10 +78,12 @@ struct SettingsView: View {
     private func behaviorSection() -> some View {
         Section {
             Toggle("Swipe actions", isOn: $swipeActionsEnabled)
+                .themedRow()
             Toggle("Contribution graphs", isOn: $contributionGraphsEnabled)
                 .onChange(of: contributionGraphsEnabled) { _, newValue in
                     ContributionWidgetContextStore.setEnabled(newValue)
                 }
+                .themedRow()
         } header: {
             Text("Behavior")
         } footer: {
@@ -101,12 +105,14 @@ struct SettingsView: View {
                 }
             }
             .alignmentGuide(.listRowSeparatorLeading) { _ in 0 }
+            .themedRow()
 
             Button {
                 showAccountSwitcher = true
             } label: {
                 Label("Manage Accounts", systemImage: "person.2")
             }
+            .themedRow()
 
             HStack {
                 Image(systemName: "lock.shield")
@@ -116,14 +122,17 @@ struct SettingsView: View {
                     .foregroundStyle(.secondary)
             }
             .alignmentGuide(.listRowSeparatorLeading) { _ in 0 }
+            .themedRow()
 
             Button("Reset App Data", role: .destructive) {
                 pendingDestructiveAction = .resetAppData
             }
+            .themedRow()
 
             Button("Sign Out", role: .destructive) {
                 pendingDestructiveAction = .signOut
             }
+            .themedRow()
         } header: {
             Text("Authentication")
         } footer: {
@@ -139,6 +148,7 @@ struct SettingsView: View {
             } label: {
                 SwiftUI.Label("About Hutch", systemImage: "info.circle")
             }
+            .themedRow()
         }
     }
 }
@@ -211,15 +221,18 @@ private struct AboutView: View {
                         .foregroundStyle(.secondary)
                 }
                 .padding(.vertical, 4)
+                .themedRow()
 
                 LabeledContent("Version", value: version)
                     .onTapGesture {
                         developerRevealCount = min(developerRevealCount + 1, 5)
                     }
+                    .themedRow()
                 LabeledContent("Build", value: build)
                     .onTapGesture {
                         developerRevealCount = min(developerRevealCount + 1, 5)
                     }
+                    .themedRow()
             } footer: {
                 Text(developerRevealFooterText)
             }
@@ -228,34 +241,41 @@ private struct AboutView: View {
                 Link(destination: URL(string: "https://sr.ht")!) {
                     SwiftUI.Label("SourceHut", systemImage: "link")
                 }
+                .themedRow()
                 Link(destination: URL(string: "https://man.sr.ht")!) {
                     SwiftUI.Label("SourceHut Manuals", systemImage: "book")
                 }
+                .themedRow()
                 Link(destination: URL(string: "https://sr.ht/~ccleberg/Hutch")!) {
                     SwiftUI.Label("Project Repository", systemImage: "folder")
                 }
+                .themedRow()
             }
 
             Section("Support") {
                 Link(destination: URL(string: "mailto:hello@cleberg.net")!) {
                     SwiftUI.Label("Email Support", systemImage: "envelope")
                 }
+                .themedRow()
             }
 
             Section("Privacy") {
                 Text("Hutch uses your SourceHut personal access token to make requests on your behalf. The token is stored locally in the iOS keychain.")
                     .font(.subheadline)
                     .foregroundStyle(.secondary)
+                    .themedRow()
 
                 Link(destination: URL(string: "https://zerolabs.sh/hutch/privacy-policy/")!) {
                     SwiftUI.Label("Privacy Policy", systemImage: "hand.raised")
                 }
+                .themedRow()
             }
 
             Section("Acknowledgements") {
                 Text("Built for SourceHut users who want quick access to repositories, builds, and tickets on iOS.")
                     .font(.subheadline)
                     .foregroundStyle(.secondary)
+                    .themedRow()
             }
 
             if developerToolsVisible {
@@ -264,18 +284,21 @@ private struct AboutView: View {
                         get: { appState.isDebugModeEnabled },
                         set: { appState.isDebugModeEnabled = $0 }
                     ))
+                    .themedRow()
 
                     NavigationLink {
                         HomePrototypeView()
                     } label: {
                         SwiftUI.Label("Home Prototype", systemImage: "house")
                     }
+                    .themedRow()
 
                     NavigationLink {
                         WorkPrototypeView()
                     } label: {
                         SwiftUI.Label("Work Prototype", systemImage: "tray.full")
                     }
+                    .themedRow()
                 } header: {
                     Text("Developer")
                 } footer: {
diff --git a/Hutch/Views/SystemStatus/SystemStatusView.swift b/Hutch/Views/SystemStatus/SystemStatusView.swift
index 08a46eb..a3ce5b3 100644
--- a/Hutch/Views/SystemStatus/SystemStatusView.swift
+++ b/Hutch/Views/SystemStatus/SystemStatusView.swift
@@ -36,6 +36,7 @@ struct SystemStatusView: View {
                     Label(staleDataMessage, systemImage: "clock.arrow.trianglehead.counterclockwise.rotate.90")
                         .font(.subheadline)
                         .foregroundStyle(.secondary)
+                        .themedRow()
                 }
             }
 
@@ -47,6 +48,7 @@ struct SystemStatusView: View {
 
             recentIncidentsSection(viewModel.recentIncidents)
         }
+        .themedList()
         .listStyle(.insetGrouped)
         .refreshable {
             await viewModel.load(forceRefresh: true)
@@ -99,6 +101,7 @@ struct SystemStatusView: View {
                 .font(.subheadline.weight(.medium))
             }
             .padding(.vertical, 4)
+            .themedRow()
         }
     }
 
@@ -119,6 +122,7 @@ struct SystemStatusView: View {
                 }
                 .padding(.vertical, 2)
             }
+            .themedRow()
         }
     }
 
@@ -129,6 +133,7 @@ struct SystemStatusView: View {
                 ForEach(incidents) { incident in
                     incidentRow(incident)
                 }
+                .themedRow()
             }
         }
     }
@@ -142,10 +147,12 @@ struct SystemStatusView: View {
                     systemImage: "clock.arrow.trianglehead.counterclockwise.rotate.90",
                     description: Text("The status feed didn’t return any recent incidents.")
                 )
+                .themedRow()
             } else {
                 ForEach(incidents) { incident in
                     incidentRow(incident)
                 }
+                .themedRow()
             }
         }
     }
diff --git a/Hutch/Views/Tickets/TicketDetailView.swift b/Hutch/Views/Tickets/TicketDetailView.swift
index 32ef89c..0028083 100644
--- a/Hutch/Views/Tickets/TicketDetailView.swift
+++ b/Hutch/Views/Tickets/TicketDetailView.swift
@@ -739,8 +739,10 @@ private struct ResolveSheet: View {
                     }
                     .pickerStyle(.inline)
                     .labelsHidden()
+                    .themedRow()
                 }
             }
+            .themedList()
             .navigationTitle("Resolve Ticket")
             .navigationBarTitleDisplayMode(.inline)
             .toolbar {
@@ -801,6 +803,7 @@ private struct AssignSheet: View {
                                 .buttonStyle(.plain)
                             }
                         }
+                        .themedRow()
                     }
                 }
 
@@ -809,6 +812,7 @@ private struct AssignSheet: View {
                         .textContentType(.username)
                         .autocorrectionDisabled()
                         .textInputAutocapitalization(.never)
+                        .themedRow()
 
                     Button("Add Assignee") {
                         let name = username.trimmingCharacters(in: .whitespacesAndNewlines)
@@ -822,8 +826,10 @@ private struct AssignSheet: View {
                         username.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
                         || viewModel.isPerformingAction
                     )
+                    .themedRow()
                 }
             }
+            .themedList()
             .navigationTitle("Assignees")
             .navigationBarTitleDisplayMode(.inline)
             .toolbar {
@@ -877,7 +883,9 @@ private struct LabelsSheet: View {
                                 }
                             }
                         }
+                        .themedRow()
                     }
+                    .themedList()
                 }
             }
             .navigationTitle("Labels")
@@ -917,11 +925,14 @@ private struct CreateLabelSheet: View {
                 Section("Label Details") {
                     TextField("Label name", text: $labelName)
                         .autocorrectionDisabled()
+                        .themedRow()
                 }
 
                 Section("Colors") {
                     ColorPicker("Background color", selection: $backgroundColor, supportsOpacity: false)
+                        .themedRow()
                     ColorPicker("Text color", selection: $foregroundColor, supportsOpacity: false)
+                        .themedRow()
                 }
 
                 Section("Preview") {
@@ -936,8 +947,10 @@ private struct CreateLabelSheet: View {
                             .clipShape(Capsule())
                         Spacer()
                     }
+                    .themedRow()
                 }
             }
+            .themedList()
             .navigationTitle("New Label")
             .navigationBarTitleDisplayMode(.inline)
             .toolbar {
diff --git a/Hutch/Views/Tickets/TicketListView.swift b/Hutch/Views/Tickets/TicketListView.swift
index e58d194..940a282 100644
--- a/Hutch/Views/Tickets/TicketListView.swift
+++ b/Hutch/Views/Tickets/TicketListView.swift
@@ -6,6 +6,7 @@ struct TicketListView: View {
 
     @AppStorage(AppStorageKeys.swipeActionsEnabled, store: .standard) private var swipeActionsEnabled = true
     @Environment(AppState.self) private var appState
+    @Environment(\.isAMOLEDTheme) private var isAMOLED
     @Environment(\.dismiss) private var dismiss
     @Environment(\.openURL) private var openURL
     @State private var tracker: TrackerSummary
@@ -277,7 +278,7 @@ struct TicketListView: View {
                     vm.deleteSavedFilter(savedFilter)
                 }
                 .listRowInsets(EdgeInsets())
-                .listRowBackground(Color.clear)
+                .listRowBackground(isAMOLED ? Color.black : Color.clear)
                 .listRowSeparator(.hidden)
 
                 ForEach(viewModel.filteredTickets) { ticket in
@@ -344,6 +345,7 @@ struct TicketListView: View {
                         await viewModel.loadMoreIfNeeded(currentItem: ticket)
                     }
                 }
+                .themedRow()
 
                 if viewModel.isLoadingMore {
                     HStack {
@@ -352,9 +354,11 @@ struct TicketListView: View {
                         Spacer()
                     }
                     .listRowSeparator(.hidden)
+                    .themedRow()
                 }
             }
         }
+        .themedList()
         .listStyle(.plain)
         .listSectionSpacing(.compact)
         .searchable(
@@ -636,10 +640,13 @@ private struct CreateTicketSheet: View {
             Form {
                 Section("Ticket Details") {
                     TextField("Title", text: $subject)
+                        .themedRow()
                     TextField("Description (optional)", text: $descriptionText, axis: .vertical)
                         .lineLimit(6...12)
+                        .themedRow()
                 }
             }
+            .themedList()
             .navigationTitle("New Ticket")
             .navigationBarTitleDisplayMode(.inline)
             .toolbar {
@@ -705,7 +712,9 @@ private struct TicketLabelsSheet: View {
                                     }
                                 }
                             }
+                            .themedRow()
                         }
+                        .themedList()
                     }
                 } else {
                     ContentUnavailableView(
@@ -921,6 +930,7 @@ private struct TicketFilterLabelsSheet: View {
                                     }
                                 }
                                 .padding(.vertical, 4)
+                                .themedRow()
                             }
                         }
 
@@ -940,8 +950,10 @@ private struct TicketFilterLabelsSheet: View {
                                 }
                                 .buttonStyle(.plain)
                             }
+                            .themedRow()
                         }
                     }
+                    .themedList()
                 }
             }
             .navigationTitle("Filter Labels")
@@ -982,8 +994,10 @@ private struct SaveTicketFilterSheet: View {
                 Section("Name") {
                     TextField("Filter name", text: $name)
                         .textInputAutocapitalization(.words)
+                        .themedRow()
                 }
             }
+            .themedList()
             .navigationTitle("Save Filter")
             .navigationBarTitleDisplayMode(.inline)
             .toolbar {
@@ -1085,6 +1099,7 @@ private struct BulkResolveSheet: View {
                     Text("\(viewModel.selectedTicketCount) ticket\(viewModel.selectedTicketCount == 1 ? "" : "s") selected")
                         .font(.subheadline)
                         .foregroundStyle(.secondary)
+                        .themedRow()
                 }
 
                 Section("Resolution") {
@@ -1095,8 +1110,10 @@ private struct BulkResolveSheet: View {
                     }
                     .pickerStyle(.inline)
                     .labelsHidden()
+                    .themedRow()
                 }
             }
+            .themedList()
             .navigationTitle("Close Tickets")
             .navigationBarTitleDisplayMode(.inline)
             .toolbar {
@@ -1133,6 +1150,7 @@ private struct BulkAssignSheet: View {
                     Text("\(viewModel.selectedTicketCount) ticket\(viewModel.selectedTicketCount == 1 ? "" : "s") selected")
                         .font(.subheadline)
                         .foregroundStyle(.secondary)
+                        .themedRow()
                 }
 
                 if let currentUser {
@@ -1146,6 +1164,7 @@ private struct BulkAssignSheet: View {
                             }
                         }
                         .disabled(viewModel.selectedTicketCount == 0 || viewModel.isPerformingAction)
+                        .themedRow()
                     }
                 }
 
@@ -1154,8 +1173,10 @@ private struct BulkAssignSheet: View {
                         .textContentType(.username)
                         .autocorrectionDisabled()
                         .textInputAutocapitalization(.never)
+                        .themedRow()
                 }
             }
+            .themedList()
             .navigationTitle("Assign Tickets")
             .navigationBarTitleDisplayMode(.inline)
             .toolbar {
diff --git a/Hutch/Views/Tickets/TrackerListView.swift b/Hutch/Views/Tickets/TrackerListView.swift
index db0fdf2..9ca3340 100644
--- a/Hutch/Views/Tickets/TrackerListView.swift
+++ b/Hutch/Views/Tickets/TrackerListView.swift
@@ -162,6 +162,7 @@ struct TrackerListView: View {
                     await viewModel.loadMoreIfNeeded(currentItem: tracker)
                 }
             }
+            .themedRow()
 
             if viewModel.isLoadingMore {
                 HStack {
@@ -170,8 +171,10 @@ struct TrackerListView: View {
                     Spacer()
                 }
                 .listRowSeparator(.hidden)
+                .themedRow()
             }
         }
+        .themedList()
         .listStyle(.plain)
         .searchable(
             text: $vm.searchText,
diff --git a/Hutch/Views/Tickets/TrackerManagementView.swift b/Hutch/Views/Tickets/TrackerManagementView.swift
index cf923b6..4e04bbd 100644
--- a/Hutch/Views/Tickets/TrackerManagementView.swift
+++ b/Hutch/Views/Tickets/TrackerManagementView.swift
@@ -654,22 +654,27 @@ struct TrackerEditorSheet: View {
                     TextField("Tracker name", text: $name)
                         .textInputAutocapitalization(.never)
                         .autocorrectionDisabled()
+                        .themedRow()
                     TextField("Short description (optional)", text: $description, axis: .vertical)
                         .lineLimit(2...4)
+                        .themedRow()
                     Picker("Visibility", selection: $visibility) {
                         Text("Public").tag(Visibility.public)
                         Text("Unlisted").tag(Visibility.unlisted)
                         Text("Private").tag(Visibility.private)
                     }
+                    .themedRow()
                 }
 
                 if let error, !error.isEmpty {
                     Section {
                         Text(error)
                             .foregroundStyle(.red)
+                            .themedRow()
                     }
                 }
             }
+            .themedList()
             .navigationTitle(title)
             .navigationBarTitleDisplayMode(.inline)
             .toolbar {
@@ -718,10 +723,12 @@ struct TrackerACLManagementSheet: View {
             List {
                 Section("Default Access") {
                     TrackerPermissionSummary(permissions: viewModel.defaultACL.permissions)
+                        .themedRow()
                     Button("Update Default ACL") {
                         editingDefaultACL = true
                     }
                     .disabled(viewModel.isSavingACL)
+                    .themedRow()
                 }
 
                 Section {
@@ -731,9 +738,11 @@ struct TrackerACLManagementSheet: View {
                             ProgressView()
                             Spacer()
                         }
+                        .themedRow()
                     } else if viewModel.acls.isEmpty {
                         Text("No tracker-specific ACLs yet.")
                             .foregroundStyle(.secondary)
+                            .themedRow()
                     } else {
                         ForEach(viewModel.acls) { entry in
                             VStack(alignment: .leading, spacing: 6) {
@@ -756,6 +765,7 @@ struct TrackerACLManagementSheet: View {
                                 .tint(.blue)
                             }
                         }
+                        .themedRow()
                     }
                 } header: {
                     Text("User ACLs")
@@ -763,6 +773,7 @@ struct TrackerACLManagementSheet: View {
                     Text("Each ACL must include all five permission flags.")
                 }
             }
+            .themedList()
             .navigationTitle("ACLs")
             .navigationBarTitleDisplayMode(.inline)
             .toolbar {
@@ -908,6 +919,7 @@ private struct TrackerACLEditorSheet: View {
                     TextField("Username or ~username", text: $username)
                         .autocorrectionDisabled()
                         .textInputAutocapitalization(.never)
+                        .themedRow()
                 }
 
                 permissionSection
@@ -916,9 +928,11 @@ private struct TrackerACLEditorSheet: View {
                     Section {
                         Text(error)
                             .foregroundStyle(.red)
+                            .themedRow()
                     }
                 }
             }
+            .themedList()
             .navigationTitle(title)
             .navigationBarTitleDisplayMode(.inline)
             .toolbar {
@@ -950,10 +964,15 @@ private struct TrackerACLEditorSheet: View {
     private var permissionSection: some View {
         Section("Permissions") {
             Toggle("Browse", isOn: $browse)
+                .themedRow()
             Toggle("Submit", isOn: $submit)
+                .themedRow()
             Toggle("Comment", isOn: $comment)
+                .themedRow()
             Toggle("Edit", isOn: $edit)
+                .themedRow()
             Toggle("Triage", isOn: $triage)
+                .themedRow()
         }
     }
 
@@ -1003,19 +1022,26 @@ private struct TrackerDefaultACLEditorSheet: View {
             Form {
                 Section("Permissions") {
                     Toggle("Browse", isOn: $browse)
+                        .themedRow()
                     Toggle("Submit", isOn: $submit)
+                        .themedRow()
                     Toggle("Comment", isOn: $comment)
+                        .themedRow()
                     Toggle("Edit", isOn: $edit)
+                        .themedRow()
                     Toggle("Triage", isOn: $triage)
+                        .themedRow()
                 }
 
                 if let error, !error.isEmpty {
                     Section {
                         Text(error)
                             .foregroundStyle(.red)
+                            .themedRow()
                     }
                 }
             }
+            .themedList()
             .navigationTitle("Default ACL")
             .navigationBarTitleDisplayMode(.inline)
             .toolbar {
@@ -1073,6 +1099,7 @@ struct TrackerLabelManagementSheet: View {
                     Text("Labels are managed here and reused throughout the tracker.")
                         .font(.footnote)
                         .foregroundStyle(.secondary)
+                        .themedRow()
                 }
 
                 if viewModel.isLoadingLabels {
@@ -1081,12 +1108,14 @@ struct TrackerLabelManagementSheet: View {
                         ProgressView()
                         Spacer()
                     }
+                    .themedRow()
                 } else if viewModel.labels.isEmpty {
                     ContentUnavailableView(
                         "No Labels",
                         systemImage: "tag",
                         description: Text("Create labels for triage and organization.")
                     )
+                    .themedRow()
                 } else {
                     ForEach(viewModel.labels) { label in
                         Button {
@@ -1110,8 +1139,10 @@ struct TrackerLabelManagementSheet: View {
                             }
                         }
                     }
+                    .themedRow()
                 }
             }
+            .themedList()
             .navigationTitle("Labels")
             .navigationBarTitleDisplayMode(.inline)
             .toolbar {
@@ -1261,8 +1292,11 @@ private struct TrackerLabelEditorSheet: View {
             Form {
                 Section("Details") {
                     TextField("Label name", text: $name)
+                        .themedRow()
                     ColorPicker("Foreground", selection: $foregroundColor, supportsOpacity: false)
+                        .themedRow()
                     ColorPicker("Background", selection: $backgroundColor, supportsOpacity: false)
+                        .themedRow()
                 }
 
                 Section("Preview") {
@@ -1274,15 +1308,18 @@ private struct TrackerLabelEditorSheet: View {
                             foregroundColor: foregroundColor.hexString
                         )
                     )
+                    .themedRow()
                 }
 
                 if let error, !error.isEmpty {
                     Section {
                         Text(error)
                             .foregroundStyle(.red)
+                            .themedRow()
                     }
                 }
             }
+            .themedList()
             .navigationTitle(title)
             .navigationBarTitleDisplayMode(.inline)
             .toolbar {
diff --git a/Hutch/Views/Work/WorkView.swift b/Hutch/Views/Work/WorkView.swift
index a7fbd1c..2326236 100644
--- a/Hutch/Views/Work/WorkView.swift
+++ b/Hutch/Views/Work/WorkView.swift
@@ -11,6 +11,7 @@ struct WorkView: View {
 
     @AppStorage(AppStorageKeys.swipeActionsEnabled, store: .standard) private var swipeActionsEnabled = true
     @Environment(AppState.self) private var appState
+    @Environment(\.isAMOLEDTheme) private var isAMOLED
     @Environment(\.scenePhase) private var scenePhase
     @State private var viewModel: HomeViewModel?
     @State private var scope: Scope = .all
@@ -74,6 +75,7 @@ struct WorkView: View {
                 }
             }
             .padding(.vertical, 2)
+            .themedRow()
         }
     }
 
@@ -88,6 +90,7 @@ struct WorkView: View {
         if workCount(viewModel) == 0 {
             Section {
                 WorkCompactMessageRow(text: "Nothing to do", systemImage: "checkmark.circle")
+                    .themedRow()
             }
         }
     }
@@ -100,7 +103,7 @@ struct WorkView: View {
                 }
             }
             .pickerStyle(.segmented)
-            .listRowBackground(Color.clear)
+            .listRowBackground(isAMOLED ? Color.black : Color.clear)
             .listRowInsets(EdgeInsets())
         }
     }
@@ -110,9 +113,11 @@ struct WorkView: View {
         Section {
             if isLoadingUnread(viewModel) {
                 WorkLoadingRow(label: "Loading unread threads")
+                    .themedRow()
             } else if viewModel.unreadInboxThreads.isEmpty {
                 if compactWhenEmpty {
                     WorkCompactMessageRow(text: "No unread threads", systemImage: "tray")
+                        .themedRow()
                 }
             } else {
                 ForEach(viewModel.unreadInboxThreads) { thread in
@@ -137,6 +142,7 @@ struct WorkView: View {
                         }
                     }
                 }
+                .themedRow()
             }
         } header: {
             Text("Unread Threads")
@@ -155,8 +161,10 @@ struct WorkView: View {
         Section {
             if viewModel.isLoadingAssignedTickets && viewModel.assignedTickets.isEmpty {
                 WorkLoadingRow(label: "Loading assigned tickets")
+                    .themedRow()
             } else if viewModel.assignedTickets.isEmpty {
                 WorkCompactMessageRow(text: "No assigned tickets", systemImage: "person.crop.circle.badge.checkmark")
+                    .themedRow()
             } else {
                 ForEach(viewModel.assignedTickets) { ticket in
                     NavigationLink {
@@ -200,6 +208,7 @@ struct WorkView: View {
                         }
                     }
                 }
+                .themedRow()
             }
         } header: {
             Text("Assigned Tickets")