Set the navigation and tab bars in the library's type
The screen titles were still system bold sans — `navigationTitle` renders through UIKit, which no SwiftUI font modifier reaches, so paperSurface() could tint the bar but never restyle its text. PaperAppearance configures UINavigationBarAppearance once at launch: serif large and inline titles in ink, and monospaced tab bar labels. One trap worth recording: configuring that appearance with configureWithOpaqueBackground() makes iOS 26 stop laying out the large title entirely — it vanishes rather than restyling, and it does so even with the font attributes removed, so it reads like a font problem when it isn't. Transparent works, and is right here anyway: every screen already paints the paper ground itself. Verified in both schemes. Suite passes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JgLHztZGaEHvS3KNeGQmRM
This commit is contained in:
co-authored by
Claude Opus 5
parent
fffb3999bf
commit
dd1c09a00c
@@ -83,11 +83,20 @@ enum Paper {
|
||||
}
|
||||
|
||||
static func dynamic(light: UInt32, dark: UInt32) -> Color {
|
||||
Color(uiColor: UIColor { traits in
|
||||
UIColor(rgb: traits.userInterfaceStyle == .dark ? dark : light)
|
||||
})
|
||||
Color(uiColor: dynamicUI(light: light, dark: dark))
|
||||
}
|
||||
|
||||
static func dynamicUI(light: UInt32, dark: UInt32) -> UIColor {
|
||||
UIColor { traits in
|
||||
UIColor(rgb: traits.userInterfaceStyle == .dark ? dark : light)
|
||||
}
|
||||
}
|
||||
|
||||
/// UIKit equivalents, for the chrome SwiftUI can't reach — chiefly the
|
||||
/// navigation bar, whose title font has no SwiftUI API at all.
|
||||
static let uiSheet = dynamicUI(light: 0xF8F5EF, dark: 0x15130F)
|
||||
static let uiInk = dynamicUI(light: 0x14110C, dark: 0xF1ECE1)
|
||||
|
||||
static func luminance(of hex: UInt32) -> Double {
|
||||
let r = Double((hex >> 16) & 0xFF) / 255
|
||||
let g = Double((hex >> 8) & 0xFF) / 255
|
||||
@@ -154,6 +163,56 @@ extension View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIKit chrome
|
||||
|
||||
/// The navigation and tab bars are UIKit underneath, and their fonts have no
|
||||
/// SwiftUI equivalent — `navigationTitle` will render in the system bold sans
|
||||
/// whatever you do to the view. Both are configured once at launch so screen
|
||||
/// titles speak the same serif as the content beneath them.
|
||||
enum PaperAppearance {
|
||||
static func apply() {
|
||||
let bar = UINavigationBarAppearance()
|
||||
// Transparent, not opaque: the screens already paint the paper ground
|
||||
// themselves, and an opaque bar config stops iOS 26 laying out the
|
||||
// large title at all.
|
||||
bar.configureWithTransparentBackground()
|
||||
bar.largeTitleTextAttributes = [
|
||||
.font: serif(34, weight: .regular),
|
||||
.foregroundColor: Paper.uiInk,
|
||||
]
|
||||
bar.titleTextAttributes = [
|
||||
.font: serif(17, weight: .medium),
|
||||
.foregroundColor: Paper.uiInk,
|
||||
]
|
||||
UINavigationBar.appearance().standardAppearance = bar
|
||||
UINavigationBar.appearance().compactAppearance = bar
|
||||
UINavigationBar.appearance().scrollEdgeAppearance = bar
|
||||
|
||||
let tabs = UITabBarAppearance()
|
||||
tabs.configureWithDefaultBackground()
|
||||
let label: [NSAttributedString.Key: Any] = [
|
||||
.font: UIFont.monospacedSystemFont(ofSize: 10, weight: .medium)
|
||||
]
|
||||
for item in [tabs.stackedLayoutAppearance,
|
||||
tabs.inlineLayoutAppearance,
|
||||
tabs.compactInlineLayoutAppearance] {
|
||||
item.normal.titleTextAttributes = label
|
||||
item.selected.titleTextAttributes = label
|
||||
}
|
||||
UITabBar.appearance().standardAppearance = tabs
|
||||
UITabBar.appearance().scrollEdgeAppearance = tabs
|
||||
}
|
||||
|
||||
/// `withDesign(.serif)` is the only route to the system serif in UIKit, and
|
||||
/// it returns nil if the design is unavailable — fall back rather than
|
||||
/// force-unwrap a font.
|
||||
private static func serif(_ size: CGFloat, weight: UIFont.Weight) -> UIFont {
|
||||
let base = UIFont.systemFont(ofSize: size, weight: weight)
|
||||
guard let descriptor = base.fontDescriptor.withDesign(.serif) else { return base }
|
||||
return UIFont(descriptor: descriptor, size: size)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Empty state
|
||||
|
||||
/// The paper equivalent of `ContentUnavailableView`, which can't be restyled —
|
||||
|
||||
Reference in New Issue
Block a user