From 2228da3eb920b6bb0f4e782638e89e06572a0424 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Tue, 11 Aug 2026 11:07:24 +0100 Subject: [PATCH] Make sure the Tab Rail's background matches the split view's detail background. (#6003) --- .../Navigation/NavigationCoordinators.swift | 19 +++++----- .../Navigation/NavigationTabCoordinator.swift | 7 ++++ ...een.testSpaceExploration-iPad-en-GB-11.png | 4 +-- ...een.testSpaceExploration-iPad-en-GB-12.png | 4 +-- .../Compound/Colors/CompoundBackground.swift | 35 +++++++++++++++++++ .../Sources/Compound/List/ListStyles.swift | 2 ++ 6 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 compound-ios/Sources/Compound/Colors/CompoundBackground.swift diff --git a/ElementX/Sources/Application/Navigation/NavigationCoordinators.swift b/ElementX/Sources/Application/Navigation/NavigationCoordinators.swift index 4559e1b8d..e84c5205d 100644 --- a/ElementX/Sources/Application/Navigation/NavigationCoordinators.swift +++ b/ElementX/Sources/Application/Navigation/NavigationCoordinators.swift @@ -385,15 +385,18 @@ private struct NavigationSplitCoordinatorView: View { /// The NavigationSplitView that will be used in non-compact layouts var navigationSplitView: some View { NavigationSplitView(columnVisibility: $navigationSplitCoordinator.columnVisibility) { - if let sidebarModule = navigationSplitCoordinator.sidebarModule { - sidebarModule.coordinator?.toPresentable() - .environment(\.isInSidebar, true) - .id(sidebarModule.id) - } else { - navigationSplitCoordinator.placeholderModule.coordinator?.toPresentable() - .environment(\.isInSidebar, true) - .id(navigationSplitCoordinator.placeholderModule.id) + Group { + if let sidebarModule = navigationSplitCoordinator.sidebarModule { + sidebarModule.coordinator?.toPresentable() + .id(sidebarModule.id) + } else { + navigationSplitCoordinator.placeholderModule.coordinator?.toPresentable() + .id(navigationSplitCoordinator.placeholderModule.id) + } } + .environment(\.isInSidebar, true) + // The tab rail's background tracks the detail module, so exclude the sidebar's background value. + .transformPreference(CompoundBackgroundPreferenceKey.self) { $0 = nil } } detail: { if let detailModule = navigationSplitCoordinator.detailModule { detailModule.coordinator?.toPresentable() diff --git a/ElementX/Sources/Application/Navigation/NavigationTabCoordinator.swift b/ElementX/Sources/Application/Navigation/NavigationTabCoordinator.swift index 0644f7c19..c92bef1ac 100644 --- a/ElementX/Sources/Application/Navigation/NavigationTabCoordinator.swift +++ b/ElementX/Sources/Application/Navigation/NavigationTabCoordinator.swift @@ -316,6 +316,7 @@ private struct NavigationTabCoordinatorView: View { @State private var window: UIWindow? @State private var isFullScreen = true @State private var isRailVisible = true + @State private var railBackgroundColor: Color = .compound.bgCanvasDefault var body: some View { tabView @@ -362,6 +363,8 @@ private struct NavigationTabCoordinatorView: View { HStack(spacing: 0) { if isRailVisible { TabRailView(navigationTabCoordinator: navigationTabCoordinator, isFullScreen: isFullScreen) + .background(railBackgroundColor.ignoresSafeArea()) + .animation(.easeInOut(duration: 0.4).disabledDuringTests(), value: railBackgroundColor) } if let module = navigationTabCoordinator.tabModules.first(where: { $0.details.tag == navigationTabCoordinator.selectedTab }) { @@ -369,6 +372,10 @@ private struct NavigationTabCoordinatorView: View { .id(module.id) } } + // Match the rail's background to the colour of the split view's detail (or the root if there isn't a split). + .onPreferenceChange(CompoundBackgroundPreferenceKey.self) { background in + railBackgroundColor = background?.colorValue ?? .compound.bgCanvasDefault + } .introspect(.window, on: .supportedVersions) { window in guard self.window !== window else { return } diff --git a/UITests/Sources/__Snapshots__/Application/userSessionScreen.testSpaceExploration-iPad-en-GB-11.png b/UITests/Sources/__Snapshots__/Application/userSessionScreen.testSpaceExploration-iPad-en-GB-11.png index 15180aec8..d00f02ff4 100644 --- a/UITests/Sources/__Snapshots__/Application/userSessionScreen.testSpaceExploration-iPad-en-GB-11.png +++ b/UITests/Sources/__Snapshots__/Application/userSessionScreen.testSpaceExploration-iPad-en-GB-11.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f3e7c2cb5fbbdbccf317f49936f2df84e9247d6e40e6cf1662c79c6bcc9ad4cd -size 358569 +oid sha256:db70cf216901b52c1f188338a6671cdf41ddcb755f65fe9ebcd8ed6cb148ff11 +size 347369 diff --git a/UITests/Sources/__Snapshots__/Application/userSessionScreen.testSpaceExploration-iPad-en-GB-12.png b/UITests/Sources/__Snapshots__/Application/userSessionScreen.testSpaceExploration-iPad-en-GB-12.png index c89ab1b34..41d816505 100644 --- a/UITests/Sources/__Snapshots__/Application/userSessionScreen.testSpaceExploration-iPad-en-GB-12.png +++ b/UITests/Sources/__Snapshots__/Application/userSessionScreen.testSpaceExploration-iPad-en-GB-12.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:550a193e6ac71694015cba80d705bcac7d90ac8b73cccdd7aae63e762aa59092 -size 266276 +oid sha256:39bd09601f6a3c332220d821b8648b35c1ff4d64ebfcd729c3941e4f119cff19 +size 261578 diff --git a/compound-ios/Sources/Compound/Colors/CompoundBackground.swift b/compound-ios/Sources/Compound/Colors/CompoundBackground.swift new file mode 100644 index 000000000..af8d78204 --- /dev/null +++ b/compound-ios/Sources/Compound/Colors/CompoundBackground.swift @@ -0,0 +1,35 @@ +// +// Copyright 2026 Element Creations Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. +// Please see LICENSE files in the repository root for full details. +// + +import SwiftUI + +/// The reported background drawn by a view. +public enum CompoundBackground: Equatable { + /// A specific background colour. + case color(Color) + /// The default canvas background. + case `default` + + public var colorValue: Color { + switch self { + case .color(let color): color + case .default: .compound.bgCanvasDefault + } + } +} + +/// A background reported up the hierarchy by a view that opts in, so that surrounding chrome (e.g. the +/// tab rail that sits alongside iOS 26's inset sidebar) can match that view's canvas. +public struct CompoundBackgroundPreferenceKey: PreferenceKey { + public static let defaultValue: CompoundBackground? = nil + + public static func reduce(value: inout CompoundBackground?, nextValue: () -> CompoundBackground?) { + if let nextValue = nextValue() { + value = nextValue + } + } +} diff --git a/compound-ios/Sources/Compound/List/ListStyles.swift b/compound-ios/Sources/Compound/List/ListStyles.swift index 3bedd0089..e058327e9 100644 --- a/compound-ios/Sources/Compound/List/ListStyles.swift +++ b/compound-ios/Sources/Compound/List/ListStyles.swift @@ -24,11 +24,13 @@ public extension View { environment(\.defaultMinListRowHeight, 48) .scrollContentBackground(.hidden) .background(Color.compound.bgSubtleSecondaryLevel0.ignoresSafeArea()) + .preference(key: CompoundBackgroundPreferenceKey.self, value: .color(.compound.bgSubtleSecondaryLevel0)) case .plain: listStyle(.plain) .environment(\.defaultMinListRowHeight, 48) .scrollContentBackground(.hidden) .background(Color.compound.bgCanvasDefault.ignoresSafeArea()) + .preference(key: CompoundBackgroundPreferenceKey.self, value: .default) } }