crossmate

A collaborative crossword app for iOS
Log | Files | Refs | LICENSE

commit 6ea98d378d7dcb99deb2edb1b98b415c53778cf9
parent b2122609e4db8d334a922ca9e46d73b085c72be1
Author: Michael Camilleri <[email protected]>
Date:   Fri,  3 Jul 2026 15:57:45 +0900

Vary announcement sizes depending on context

Diffstat:
MCrossmate/Views/Components/AnnouncementBanner.swift | 51+++++++++++++++++++++++++++++++++++++++++----------
MCrossmate/Views/GameList/GameListView.swift | 2+-
2 files changed, 42 insertions(+), 11 deletions(-)

diff --git a/Crossmate/Views/Components/AnnouncementBanner.swift b/Crossmate/Views/Components/AnnouncementBanner.swift @@ -4,6 +4,39 @@ import SwiftUI /// and body text over a neutral material background. Manual announcements get /// a close affordance; transient and sticky announcements do not. struct AnnouncementBanner: View { + enum ContentSize { + case compact + case prominent + + var childSpacing: CGFloat { + switch self { + case .compact: return 8 + case .prominent: return 10 + } + } + + var iconFont: Font { + switch self { + case .compact: return .footnote.weight(.semibold) + case .prominent: return .subheadline.weight(.semibold) + } + } + + var titleFont: Font { + switch self { + case .compact: return .subheadline.weight(.semibold) + case .prominent: return .callout.weight(.semibold) + } + } + + var bodyFont: Font { + switch self { + case .compact: return .subheadline + case .prominent: return .callout + } + } + } + let announcement: Announcement /// When true the banner stretches to the height its container proposes /// (PuzzleView's fixed-height header slot); by default it hugs its text, @@ -12,6 +45,9 @@ struct AnnouncementBanner: View { /// Whether to show the leading severity icon. The Settings tips archive /// turns it off — the rail alone carries the tip styling there. var showsIcon = true + /// Visual density for the banner content. Puzzle-header announcements use + /// the compact default; Game List announcements opt into larger text. + var contentSize: ContentSize = .compact /// Fill behind the banner. Defaults to the inset-grouped section fill so /// banners read as solid cards on the grouped Game List and in the Settings /// tips archive; override per surface if a different fill is wanted. @@ -20,12 +56,10 @@ struct AnnouncementBanner: View { var body: some View { let severityColor = tint(for: announcement.severity) - // 10pt between children so the icon gets the same air on its trailing - // side as it has between itself and the severity rail. - HStack(alignment: .center, spacing: 10) { + HStack(alignment: .center, spacing: contentSize.childSpacing) { if showsIcon { Image(systemName: iconName(for: announcement.severity)) - .font(.subheadline.weight(.semibold)) + .font(contentSize.iconFont) .foregroundStyle(severityColor) .frame(width: 24) .accessibilityHidden(true) @@ -34,11 +68,11 @@ struct AnnouncementBanner: View { VStack(alignment: .leading, spacing: 3) { if let title = announcement.title, !title.isEmpty { Text(title) - .font(.callout.weight(.semibold)) + .font(contentSize.titleFont) .lineLimit(1) } Text(announcement.body) - .font(.callout) + .font(contentSize.bodyFont) .foregroundStyle(.primary) .multilineTextAlignment(.leading) .lineLimit(3) @@ -61,10 +95,7 @@ struct AnnouncementBanner: View { .padding(.horizontal, 10) .padding(.vertical, 10) // Without the icon the text would otherwise crowd the severity rail; - // give it a little extra breathing room on the leading edge. With the - // icon, the extra 4pt makes the rail-to-icon gap match the 10pt - // HStack spacing on the icon's other side (the rail eats 4pt of the - // 10pt base padding). + // give it a little extra breathing room on the leading edge. .padding(.leading, showsIcon ? 4 : 12) .frame( maxWidth: .infinity, diff --git a/Crossmate/Views/GameList/GameListView.swift b/Crossmate/Views/GameList/GameListView.swift @@ -73,7 +73,7 @@ struct GameListView: View { var body: some View { VStack(spacing: 0) { if let announcement = announcements.currentGlobal() { - AnnouncementBanner(announcement: announcement) { + AnnouncementBanner(announcement: announcement, contentSize: .prominent) { dismissAnnouncement(announcement) } .padding(.horizontal)