From 3307ba5433bb939c5228af01c432e537cd6cad98 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Sat, 31 Jan 2026 11:16:38 -0600 Subject: [PATCH] Signed-off-by: Matt Bruce --- PRD.md | 8 +--- README.md | 2 +- .../Features/Alarms/Views/AddAlarmView.swift | 12 ++--- .../Views/Components/AlarmRowView.swift | 9 ++-- .../Views/Components/EmptyAlarmsView.swift | 3 +- .../Views/Components/LabelEditView.swift | 7 +-- .../NotificationMessageEditView.swift | 11 ++--- .../Components/SnoozeSelectionView.swift | 2 +- .../Views/Components/SoundSelectionView.swift | 4 +- .../Components/TimeUntilAlarmSection.swift | 2 +- .../Features/Alarms/Views/EditAlarmView.swift | 12 ++--- .../Features/Clock/State/ClockViewModel.swift | 3 +- .../Components/ClockDisplayContainer.swift | 5 ++- .../Components/ClockOverlayContainer.swift | 7 +-- .../Views/Components/TimeDisplayView.swift | 5 ++- .../Views/Components/TopOverlayView.swift | 5 ++- .../Views/Components/SoundCategoryView.swift | 45 ++++++++++--------- .../Views/Components/SoundControlView.swift | 31 ++++++------- .../Features/Noise/Views/NoiseView.swift | 25 ++++++----- .../Shared/Design/BrandingConfig.swift | 6 +-- .../Shared/Design/Design+NoiseClock.swift | 13 ++++++ .../{Theme => Design}/NoiseClockTheme.swift | 0 .../Shared/Design/UIConstants+Animation.swift | 19 -------- .../Shared/Design/UIConstants+Colors.swift | 21 --------- .../Shared/Design/UIConstants+Layout.swift | 45 ------------------- TheNoiseClock/Shared/Design/UIConstants.swift | 11 ----- .../Shared/Extensions/View+Extensions.swift | 21 ++++----- 27 files changed, 130 insertions(+), 204 deletions(-) create mode 100644 TheNoiseClock/Shared/Design/Design+NoiseClock.swift rename TheNoiseClock/Shared/{Theme => Design}/NoiseClockTheme.swift (100%) delete mode 100644 TheNoiseClock/Shared/Design/UIConstants+Animation.swift delete mode 100644 TheNoiseClock/Shared/Design/UIConstants+Colors.swift delete mode 100644 TheNoiseClock/Shared/Design/UIConstants+Layout.swift delete mode 100644 TheNoiseClock/Shared/Design/UIConstants.swift diff --git a/PRD.md b/PRD.md index 8322831..e67ff36 100644 --- a/PRD.md +++ b/PRD.md @@ -376,17 +376,13 @@ TheNoiseClock/ │ │ ├── Design/ │ │ │ ├── BrandingConfig.swift # Bedrock branding + launch config │ │ │ ├── AppConstants.swift # App-wide constants and configuration -│ │ │ ├── UIConstants.swift # UI constants namespace -│ │ │ ├── UIConstants+Colors.swift # Theme-backed colors -│ │ │ ├── UIConstants+Layout.swift # Spacing/radius/opacity -│ │ │ ├── UIConstants+Animation.swift # Animation curves +│ │ │ ├── NoiseClockTheme.swift # Bedrock app color theme +│ │ │ ├── Design+NoiseClock.swift # App-specific Design extensions │ │ │ └── Fonts/ │ │ │ ├── Font.Design.swift │ │ │ ├── Font.Weight.swift │ │ │ ├── FontFamily.swift │ │ │ └── FontUtils.swift -│ │ ├── Theme/ -│ │ │ └── NoiseClockTheme.swift # Bedrock app color theme │ │ ├── Extensions/ │ │ │ ├── Color+Extensions.swift # Color utilities and extensions │ │ │ ├── Date+Extensions.swift # Date formatting and utilities diff --git a/README.md b/README.md index d2dfdad..25d6b0a 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ xcodebuild -project TheNoiseClock/TheNoiseClock.xcodeproj -scheme TheNoiseClock The app uses Bedrock for theme tokens, settings UI, and the launch experience. -- Theme: `TheNoiseClock/Shared/Theme/NoiseClockTheme.swift` +- Theme: `TheNoiseClock/Shared/Design/NoiseClockTheme.swift` - Branding config: `TheNoiseClock/Shared/Design/BrandingConfig.swift` - Launch screen: `TheNoiseClock/Resources/LaunchScreen.storyboard` diff --git a/TheNoiseClock/Features/Alarms/Views/AddAlarmView.swift b/TheNoiseClock/Features/Alarms/Views/AddAlarmView.swift index de8b771..c40d98a 100644 --- a/TheNoiseClock/Features/Alarms/Views/AddAlarmView.swift +++ b/TheNoiseClock/Features/Alarms/Views/AddAlarmView.swift @@ -37,7 +37,7 @@ struct AddAlarmView: View { NavigationLink(destination: LabelEditView(label: $alarmLabel)) { HStack { Image(systemName: "textformat") - .foregroundColor(UIConstants.Colors.accentColor) + .foregroundColor(AppAccent.primary) .frame(width: 24) Text("Label") Spacer() @@ -50,7 +50,7 @@ struct AddAlarmView: View { NavigationLink(destination: NotificationMessageEditView(message: $notificationMessage)) { HStack { Image(systemName: "message") - .foregroundColor(UIConstants.Colors.accentColor) + .foregroundColor(AppAccent.primary) .frame(width: 24) Text("Message") Spacer() @@ -64,7 +64,7 @@ struct AddAlarmView: View { NavigationLink(destination: SoundSelectionView(selectedSound: $selectedSoundName)) { HStack { Image(systemName: "music.note") - .foregroundColor(UIConstants.Colors.accentColor) + .foregroundColor(AppAccent.primary) .frame(width: 24) Text("Sound") Spacer() @@ -77,7 +77,7 @@ struct AddAlarmView: View { NavigationLink(destination: SnoozeSelectionView(snoozeDuration: $snoozeDuration)) { HStack { Image(systemName: "clock.arrow.circlepath") - .foregroundColor(UIConstants.Colors.accentColor) + .foregroundColor(AppAccent.primary) .frame(width: 24) Text("Snooze") Spacer() @@ -96,7 +96,7 @@ struct AddAlarmView: View { Button("Cancel") { isPresented = false } - .foregroundColor(UIConstants.Colors.accentColor) + .foregroundColor(AppAccent.primary) } ToolbarItem(placement: .navigationBarTrailing) { @@ -116,7 +116,7 @@ struct AddAlarmView: View { isPresented = false } } - .foregroundColor(UIConstants.Colors.accentColor) + .foregroundColor(AppAccent.primary) .fontWeight(.semibold) } } diff --git a/TheNoiseClock/Features/Alarms/Views/Components/AlarmRowView.swift b/TheNoiseClock/Features/Alarms/Views/Components/AlarmRowView.swift index 495ade5..ed72ae9 100644 --- a/TheNoiseClock/Features/Alarms/Views/Components/AlarmRowView.swift +++ b/TheNoiseClock/Features/Alarms/Views/Components/AlarmRowView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Bedrock /// Component for displaying individual alarm row struct AlarmRowView: View { @@ -18,18 +19,18 @@ struct AlarmRowView: View { // MARK: - Body var body: some View { HStack { - VStack(alignment: .leading, spacing: UIConstants.Spacing.extraSmall) { + VStack(alignment: .leading, spacing: Design.Spacing.xSmall) { Text(alarm.formattedTime()) .font(.headline) - .foregroundColor(UIConstants.Colors.primaryText) + .foregroundColor(AppTextColors.primary) Text(alarm.label) .font(.subheadline) - .foregroundColor(UIConstants.Colors.secondaryText) + .foregroundColor(AppTextColors.secondary) Text("• \(AlarmSoundService.shared.getSoundDisplayName(alarm.soundName))") .font(.caption) - .foregroundColor(UIConstants.Colors.secondaryText) + .foregroundColor(AppTextColors.secondary) } Spacer() diff --git a/TheNoiseClock/Features/Alarms/Views/Components/EmptyAlarmsView.swift b/TheNoiseClock/Features/Alarms/Views/Components/EmptyAlarmsView.swift index 17ef39b..05cc5f5 100644 --- a/TheNoiseClock/Features/Alarms/Views/Components/EmptyAlarmsView.swift +++ b/TheNoiseClock/Features/Alarms/Views/Components/EmptyAlarmsView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Bedrock /// Empty state view for when no alarms are configured struct EmptyAlarmsView: View { @@ -15,7 +16,7 @@ struct EmptyAlarmsView: View { // MARK: - Body var body: some View { - VStack(spacing: UIConstants.Spacing.medium) { + VStack(spacing: Design.Spacing.medium) { // Icon Image(systemName: "alarm") .font(.largeTitle) diff --git a/TheNoiseClock/Features/Alarms/Views/Components/LabelEditView.swift b/TheNoiseClock/Features/Alarms/Views/Components/LabelEditView.swift index 20a0b5a..c7b8092 100644 --- a/TheNoiseClock/Features/Alarms/Views/Components/LabelEditView.swift +++ b/TheNoiseClock/Features/Alarms/Views/Components/LabelEditView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Bedrock /// View for editing alarm label struct LabelEditView: View { @@ -13,15 +14,15 @@ struct LabelEditView: View { @Environment(\.dismiss) private var dismiss var body: some View { - VStack(spacing: UIConstants.Spacing.large) { + VStack(spacing: Design.Spacing.large) { TextField("Alarm Label", text: $label) .textFieldStyle(RoundedBorderTextFieldStyle()) - .contentPadding(horizontal: UIConstants.Spacing.large) + .contentPadding(horizontal: Design.Spacing.large) Spacer() } .navigationTitle("Label") .navigationBarTitleDisplayMode(.inline) - .contentPadding(vertical: UIConstants.Spacing.large) + .contentPadding(vertical: Design.Spacing.large) } } diff --git a/TheNoiseClock/Features/Alarms/Views/Components/NotificationMessageEditView.swift b/TheNoiseClock/Features/Alarms/Views/Components/NotificationMessageEditView.swift index b68af2b..6d3a74b 100644 --- a/TheNoiseClock/Features/Alarms/Views/Components/NotificationMessageEditView.swift +++ b/TheNoiseClock/Features/Alarms/Views/Components/NotificationMessageEditView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Bedrock /// View for editing alarm notification message struct NotificationMessageEditView: View { @@ -13,13 +14,13 @@ struct NotificationMessageEditView: View { @Environment(\.dismiss) private var dismiss var body: some View { - VStack(spacing: UIConstants.Spacing.large) { + VStack(spacing: Design.Spacing.large) { TextField("Notification message", text: $message) .textFieldStyle(RoundedBorderTextFieldStyle()) - .contentPadding(horizontal: UIConstants.Spacing.large) + .contentPadding(horizontal: Design.Spacing.large) // Preview section - VStack(alignment: .leading, spacing: UIConstants.Spacing.small) { + VStack(alignment: .leading, spacing: Design.Spacing.small) { Text("Preview:") .font(.headline) .foregroundColor(.secondary) @@ -37,13 +38,13 @@ struct NotificationMessageEditView: View { .background(Color(.systemGray6)) .cornerRadius(8) } - .contentPadding(horizontal: UIConstants.Spacing.large) + .contentPadding(horizontal: Design.Spacing.large) Spacer() } .navigationTitle("Message") .navigationBarTitleDisplayMode(.inline) - .contentPadding(vertical: UIConstants.Spacing.large) + .contentPadding(vertical: Design.Spacing.large) } } diff --git a/TheNoiseClock/Features/Alarms/Views/Components/SnoozeSelectionView.swift b/TheNoiseClock/Features/Alarms/Views/Components/SnoozeSelectionView.swift index 8bded51..85f590c 100644 --- a/TheNoiseClock/Features/Alarms/Views/Components/SnoozeSelectionView.swift +++ b/TheNoiseClock/Features/Alarms/Views/Components/SnoozeSelectionView.swift @@ -23,7 +23,7 @@ struct SnoozeSelectionView: View { Spacer() if snoozeDuration == duration { Image(systemName: "checkmark") - .foregroundColor(UIConstants.Colors.accentColor) + .foregroundColor(AppAccent.primary) } } .contentShape(Rectangle()) diff --git a/TheNoiseClock/Features/Alarms/Views/Components/SoundSelectionView.swift b/TheNoiseClock/Features/Alarms/Views/Components/SoundSelectionView.swift index af2bf63..1ad6901 100644 --- a/TheNoiseClock/Features/Alarms/Views/Components/SoundSelectionView.swift +++ b/TheNoiseClock/Features/Alarms/Views/Components/SoundSelectionView.swift @@ -30,7 +30,7 @@ struct SoundSelectionView: View { Spacer() if selectedSound == sound.fileName { Image(systemName: "checkmark") - .foregroundColor(UIConstants.Colors.accentColor) + .foregroundColor(AppAccent.primary) } } .contentShape(Rectangle()) @@ -56,7 +56,7 @@ struct SoundSelectionView: View { } }) { Image(systemName: isPlaying && currentlyPlayingSound == selectedSound ? "stop.fill" : "play.fill") - .foregroundColor(UIConstants.Colors.accentColor) + .foregroundColor(AppAccent.primary) } } } diff --git a/TheNoiseClock/Features/Alarms/Views/Components/TimeUntilAlarmSection.swift b/TheNoiseClock/Features/Alarms/Views/Components/TimeUntilAlarmSection.swift index bde57bd..c984fc1 100644 --- a/TheNoiseClock/Features/Alarms/Views/Components/TimeUntilAlarmSection.swift +++ b/TheNoiseClock/Features/Alarms/Views/Components/TimeUntilAlarmSection.swift @@ -15,7 +15,7 @@ struct TimeUntilAlarmSection: View { VStack(spacing: 4) { HStack { Image(systemName: "calendar") - .foregroundColor(UIConstants.Colors.accentColor) + .foregroundColor(AppAccent.primary) Text(timeUntilAlarm) .font(.subheadline) .foregroundColor(.secondary) diff --git a/TheNoiseClock/Features/Alarms/Views/EditAlarmView.swift b/TheNoiseClock/Features/Alarms/Views/EditAlarmView.swift index c3b685e..f46db49 100644 --- a/TheNoiseClock/Features/Alarms/Views/EditAlarmView.swift +++ b/TheNoiseClock/Features/Alarms/Views/EditAlarmView.swift @@ -55,7 +55,7 @@ struct EditAlarmView: View { NavigationLink(destination: LabelEditView(label: $alarmLabel)) { HStack { Image(systemName: "textformat") - .foregroundColor(UIConstants.Colors.accentColor) + .foregroundColor(AppAccent.primary) .frame(width: 24) Text("Label") Spacer() @@ -68,7 +68,7 @@ struct EditAlarmView: View { NavigationLink(destination: NotificationMessageEditView(message: $notificationMessage)) { HStack { Image(systemName: "message") - .foregroundColor(UIConstants.Colors.accentColor) + .foregroundColor(AppAccent.primary) .frame(width: 24) Text("Message") Spacer() @@ -82,7 +82,7 @@ struct EditAlarmView: View { NavigationLink(destination: SoundSelectionView(selectedSound: $selectedSoundName)) { HStack { Image(systemName: "music.note") - .foregroundColor(UIConstants.Colors.accentColor) + .foregroundColor(AppAccent.primary) .frame(width: 24) Text("Sound") Spacer() @@ -95,7 +95,7 @@ struct EditAlarmView: View { NavigationLink(destination: SnoozeSelectionView(snoozeDuration: $snoozeDuration)) { HStack { Image(systemName: "clock.arrow.circlepath") - .foregroundColor(UIConstants.Colors.accentColor) + .foregroundColor(AppAccent.primary) .frame(width: 24) Text("Snooze") Spacer() @@ -114,7 +114,7 @@ struct EditAlarmView: View { Button("Cancel") { dismiss() } - .foregroundColor(UIConstants.Colors.accentColor) + .foregroundColor(AppAccent.primary) } ToolbarItem(placement: .navigationBarTrailing) { @@ -136,7 +136,7 @@ struct EditAlarmView: View { dismiss() } } - .foregroundColor(UIConstants.Colors.accentColor) + .foregroundColor(AppAccent.primary) .fontWeight(.semibold) } } diff --git a/TheNoiseClock/Features/Clock/State/ClockViewModel.swift b/TheNoiseClock/Features/Clock/State/ClockViewModel.swift index 11a8c28..0e0b6f3 100644 --- a/TheNoiseClock/Features/Clock/State/ClockViewModel.swift +++ b/TheNoiseClock/Features/Clock/State/ClockViewModel.swift @@ -10,6 +10,7 @@ import Combine import Observation import AudioPlaybackKit import SwiftUI +import Bedrock /// ViewModel for clock display and management @Observable @@ -60,7 +61,7 @@ class ClockViewModel { // MARK: - Public Interface func toggleDisplayMode() { - withAnimation(UIConstants.AnimationCurves.bouncy) { + withAnimation(Design.Animation.spring(bounce: Design.Animation.springBounce)) { isDisplayMode.toggle() } diff --git a/TheNoiseClock/Features/Clock/Views/Components/ClockDisplayContainer.swift b/TheNoiseClock/Features/Clock/Views/Components/ClockDisplayContainer.swift index e20bf49..e2b8d10 100644 --- a/TheNoiseClock/Features/Clock/Views/Components/ClockDisplayContainer.swift +++ b/TheNoiseClock/Features/Clock/Views/Components/ClockDisplayContainer.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Bedrock /// Container component that handles the main clock display layout and scaling struct ClockDisplayContainer: View { @@ -20,7 +21,7 @@ struct ClockDisplayContainer: View { return GeometryReader { geometry in let isPortrait = geometry.size.height >= geometry.size.width let hasOverlay = style.showBattery || style.showDate - let topSpacing = hasOverlay ? (isPortrait ? UIConstants.Spacing.huge : UIConstants.Spacing.large) : 0 + let topSpacing = hasOverlay ? (isPortrait ? Design.Spacing.xxLarge : Design.Spacing.large) : 0 VStack(spacing: 0) { // Top spacing to account for overlay @@ -50,7 +51,7 @@ struct ClockDisplayContainer: View { Spacer() } - .animation(UIConstants.AnimationCurves.smooth, value: isDisplayMode) + .animation(.smooth(duration: Design.Animation.standard), value: isDisplayMode) } } } diff --git a/TheNoiseClock/Features/Clock/Views/Components/ClockOverlayContainer.swift b/TheNoiseClock/Features/Clock/Views/Components/ClockOverlayContainer.swift index 919b90c..945a801 100644 --- a/TheNoiseClock/Features/Clock/Views/Components/ClockOverlayContainer.swift +++ b/TheNoiseClock/Features/Clock/Views/Components/ClockOverlayContainer.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Bedrock /// Container component that handles the positioning and display of top overlays struct ClockOverlayContainer: View { @@ -20,12 +21,12 @@ struct ClockOverlayContainer: View { TopOverlayView( showBattery: style.showBattery, showDate: style.showDate, - color: style.digitColor.opacity(UIConstants.Opacity.primary), + color: style.digitColor.opacity(Design.Opacity.heavy), opacity: style.overlayOpacity, dateFormat: style.dateFormat ) - .padding(.top, UIConstants.Spacing.small) - .padding(.horizontal, UIConstants.Spacing.large) + .padding(.top, Design.Spacing.small) + .padding(.horizontal, Design.Spacing.large) .transition(.opacity) } Spacer() diff --git a/TheNoiseClock/Features/Clock/Views/Components/TimeDisplayView.swift b/TheNoiseClock/Features/Clock/Views/Components/TimeDisplayView.swift index 1bea7bc..fdfd1fd 100644 --- a/TheNoiseClock/Features/Clock/Views/Components/TimeDisplayView.swift +++ b/TheNoiseClock/Features/Clock/Views/Components/TimeDisplayView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Bedrock /// Component for displaying segmented time with customizable formatting struct TimeDisplayView: View { @@ -132,8 +133,8 @@ struct TimeDisplayView: View { } .offset(y: portraitMode && forceHorizontalMode ? -containerSize.height * 0.10 : 0) // Push up in horizontal mode .scaleEffect(finalScale, anchor: .center) - .animation(UIConstants.AnimationCurves.smooth, value: finalScale) - .animation(UIConstants.AnimationCurves.smooth, value: showSeconds) // Smooth animation for seconds toggle + .animation(.smooth(duration: Design.Animation.standard), value: finalScale) + .animation(.smooth(duration: Design.Animation.standard), value: showSeconds) // Smooth animation for seconds toggle .minimumScaleFactor(0.1) .clipped() // Prevent overflow beyond bounds } diff --git a/TheNoiseClock/Features/Clock/Views/Components/TopOverlayView.swift b/TheNoiseClock/Features/Clock/Views/Components/TopOverlayView.swift index fad59a1..25d032e 100644 --- a/TheNoiseClock/Features/Clock/Views/Components/TopOverlayView.swift +++ b/TheNoiseClock/Features/Clock/Views/Components/TopOverlayView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Bedrock /// Component for displaying top overlay with battery and date information struct TopOverlayView: View { @@ -37,8 +38,8 @@ struct TopOverlayView: View { ) } } - .padding(.horizontal, UIConstants.Spacing.medium) - .padding(.vertical, UIConstants.Spacing.small) + .padding(.horizontal, Design.Spacing.medium) + .padding(.vertical, Design.Spacing.small) .transition(.opacity) .onAppear { batteryService.startMonitoring() diff --git a/TheNoiseClock/Features/Noise/Views/Components/SoundCategoryView.swift b/TheNoiseClock/Features/Noise/Views/Components/SoundCategoryView.swift index 2a6be2a..3cc5a1e 100644 --- a/TheNoiseClock/Features/Noise/Views/Components/SoundCategoryView.swift +++ b/TheNoiseClock/Features/Noise/Views/Components/SoundCategoryView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Bedrock import AudioPlaybackKit /// Category-based sound selection view with grid layout @@ -67,7 +68,7 @@ struct SoundCategoryView: View { // MARK: - Body var body: some View { - VStack(spacing: UIConstants.Spacing.medium) { + VStack(spacing: Design.Spacing.medium) { // Search Bar searchBar @@ -79,7 +80,7 @@ struct SoundCategoryView: View { soundGrid } } - .padding(.top, UIConstants.Spacing.medium) + .padding(.top, Design.Spacing.medium) } // MARK: - Subviews @@ -94,15 +95,15 @@ struct SoundCategoryView: View { .autocorrectionDisabled() .textInputAutocapitalization(.never) } - .padding(.horizontal, UIConstants.Spacing.medium) - .padding(.vertical, UIConstants.Spacing.small) + .padding(.horizontal, Design.Spacing.medium) + .padding(.vertical, Design.Spacing.small) .background(Color(.systemGray6)) .cornerRadius(10) } private var categoryTabs: some View { ScrollView(.horizontal, showsIndicators: false) { - HStack(spacing: UIConstants.Spacing.small) { + HStack(spacing: Design.Spacing.small) { ForEach(categories) { category in CategoryTab( title: category.displayName, @@ -113,12 +114,12 @@ struct SoundCategoryView: View { } } } - .padding(.horizontal, UIConstants.Spacing.medium) + .padding(.horizontal, Design.Spacing.medium) } } private var soundGrid: some View { - LazyVStack(spacing: UIConstants.Spacing.small) { + LazyVStack(spacing: Design.Spacing.small) { ForEach(filteredSounds) { sound in SoundCard( sound: sound, @@ -134,7 +135,7 @@ struct SoundCategoryView: View { ) } } - .padding(.horizontal, UIConstants.Spacing.medium) + .padding(.horizontal, Design.Spacing.medium) } } @@ -157,10 +158,10 @@ struct CategoryTab: View { .foregroundColor(.secondary) } } - .padding(.horizontal, UIConstants.Spacing.medium) - .padding(.vertical, UIConstants.Spacing.small) - .background(isSelected ? UIConstants.Colors.accentColor : Color(.systemGray6)) - .foregroundColor(isSelected ? .white : UIConstants.Colors.primaryText) + .padding(.horizontal, Design.Spacing.medium) + .padding(.vertical, Design.Spacing.small) + .background(isSelected ? AppAccent.primary : Color(.systemGray6)) + .foregroundColor(isSelected ? .white : AppTextColors.primary) .cornerRadius(20) } .buttonStyle(.plain) @@ -175,20 +176,20 @@ struct SoundCard: View { let onPreview: () -> Void var body: some View { - HStack(spacing: UIConstants.Spacing.medium) { + HStack(spacing: Design.Spacing.medium) { // Sound Icon (Left) ZStack { Circle() - .fill(isSelected ? UIConstants.Colors.accentColor : Color(.systemGray5)) + .fill(isSelected ? AppAccent.primary : Color(.systemGray5)) .frame(width: 50, height: 50) Image(systemName: soundIcon) .font(.title3) - .foregroundColor(isSelected ? .white : UIConstants.Colors.primaryText) + .foregroundColor(isSelected ? .white : AppTextColors.primary) if isPreviewing { Circle() - .stroke(UIConstants.Colors.accentColor, lineWidth: 2) + .stroke(AppAccent.primary, lineWidth: 2) .frame(width: 58, height: 58) .scaleEffect(1.02) .animation(.easeInOut(duration: 0.5).repeatForever(autoreverses: true), value: isPreviewing) @@ -200,7 +201,7 @@ struct SoundCard: View { // Sound Name Text(sound.name) .font(.subheadline.weight(.medium)) - .foregroundColor(UIConstants.Colors.primaryText) + .foregroundColor(AppTextColors.primary) .lineLimit(1) // Description @@ -216,7 +217,7 @@ struct SoundCard: View { .foregroundColor(.white) .padding(.horizontal, 6) .padding(.vertical, 2) - .background(UIConstants.Colors.accentColor, in: Capsule()) + .background(AppAccent.primary, in: Capsule()) Spacer() } @@ -224,14 +225,14 @@ struct SoundCard: View { Spacer() } - .padding(.horizontal, UIConstants.Spacing.medium) - .padding(.vertical, UIConstants.Spacing.small) + .padding(.horizontal, Design.Spacing.medium) + .padding(.vertical, Design.Spacing.small) .background( RoundedRectangle(cornerRadius: 12) - .fill(isSelected ? UIConstants.Colors.accentColor.opacity(0.1) : Color(.systemBackground)) + .fill(isSelected ? AppAccent.primary.opacity(0.1) : Color(.systemBackground)) .overlay( RoundedRectangle(cornerRadius: 12) - .stroke(isSelected ? UIConstants.Colors.accentColor : Color.clear, lineWidth: 2) + .stroke(isSelected ? AppAccent.primary : Color.clear, lineWidth: 2) ) ) .onTapGesture { diff --git a/TheNoiseClock/Features/Noise/Views/Components/SoundControlView.swift b/TheNoiseClock/Features/Noise/Views/Components/SoundControlView.swift index 0b9db2e..18e90a9 100644 --- a/TheNoiseClock/Features/Noise/Views/Components/SoundControlView.swift +++ b/TheNoiseClock/Features/Noise/Views/Components/SoundControlView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Bedrock import AudioPlaybackKit /// Component for audio playback controls @@ -19,18 +20,18 @@ struct SoundControlView: View { // MARK: - Body var body: some View { - VStack(spacing: UIConstants.Spacing.medium) { + VStack(spacing: Design.Spacing.medium) { // Sound info header if let sound = selectedSound { HStack { VStack(alignment: .leading, spacing: 4) { Text(sound.name) .font(.headline.weight(.semibold)) - .foregroundColor(UIConstants.Colors.primaryText) + .foregroundColor(AppTextColors.primary) Text(sound.description) .font(.caption) - .foregroundColor(UIConstants.Colors.secondaryText) + .foregroundColor(AppTextColors.secondary) .lineLimit(2) } @@ -42,10 +43,10 @@ struct SoundControlView: View { .foregroundColor(.white) .padding(.horizontal, 8) .padding(.vertical, 4) - .background(UIConstants.Colors.accentColor, in: Capsule()) + .background(AppAccent.primary, in: Capsule()) } - .contentPadding(horizontal: UIConstants.Spacing.medium, vertical: UIConstants.Spacing.small) - .background(UIConstants.Colors.overlayBackground, in: RoundedRectangle(cornerRadius: UIConstants.CornerRadius.medium)) + .contentPadding(horizontal: Design.Spacing.medium, vertical: Design.Spacing.small) + .background(AppSurface.overlay, in: RoundedRectangle(cornerRadius: Design.CornerRadius.large)) } // Main control button @@ -56,7 +57,7 @@ struct SoundControlView: View { onPlay(sound) } }) { - HStack(spacing: UIConstants.Spacing.small) { + HStack(spacing: Design.Spacing.small) { Image(systemName: isPlaying ? "stop.fill" : "play.fill") .font(.title2.weight(.semibold)) .foregroundColor(.white) @@ -65,11 +66,11 @@ struct SoundControlView: View { .font(.headline.weight(.semibold)) .foregroundColor(.white) } - .contentPadding(horizontal: UIConstants.Spacing.large, vertical: UIConstants.Spacing.medium) + .contentPadding(horizontal: Design.Spacing.large, vertical: Design.Spacing.medium) .background( - RoundedRectangle(cornerRadius: UIConstants.CornerRadius.large) - .fill(isPlaying ? Color.red : UIConstants.Colors.accentColor) - .shadow(color: (isPlaying ? Color.red : UIConstants.Colors.accentColor).opacity(0.3), radius: 8, x: 0, y: 4) + RoundedRectangle(cornerRadius: Design.CornerRadius.appLarge) + .fill(isPlaying ? Color.red : AppAccent.primary) + .shadow(color: (isPlaying ? Color.red : AppAccent.primary).opacity(0.3), radius: 8, x: 0, y: 4) ) } .disabled(selectedSound == nil) @@ -79,11 +80,11 @@ struct SoundControlView: View { .animation(.easeInOut(duration: 0.2), value: selectedSound) } .frame(maxWidth: 400) // Reasonable max width for iPad - .padding(UIConstants.Spacing.medium) - .background(UIConstants.Colors.overlayBackground, in: RoundedRectangle(cornerRadius: UIConstants.CornerRadius.large)) + .padding(Design.Spacing.medium) + .background(AppSurface.overlay, in: RoundedRectangle(cornerRadius: Design.CornerRadius.appLarge)) .overlay( - RoundedRectangle(cornerRadius: UIConstants.CornerRadius.large) - .stroke(UIConstants.Colors.overlayBorder, lineWidth: UIConstants.BorderWidth.normal) + RoundedRectangle(cornerRadius: Design.CornerRadius.appLarge) + .stroke(AppBorder.subtle, lineWidth: Design.LineWidth.thin) ) } } diff --git a/TheNoiseClock/Features/Noise/Views/NoiseView.swift b/TheNoiseClock/Features/Noise/Views/NoiseView.swift index 7baf583..cb43e85 100644 --- a/TheNoiseClock/Features/Noise/Views/NoiseView.swift +++ b/TheNoiseClock/Features/Noise/Views/NoiseView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Bedrock import AudioPlaybackKit /// Main noise/audio player view @@ -57,7 +58,7 @@ struct NoiseView: View { private var portraitLayout: some View { VStack(spacing: 0) { // Fixed header - VStack(alignment: .leading, spacing: UIConstants.Spacing.medium) { + VStack(alignment: .leading, spacing: Design.Spacing.medium) { Text("Ambient Sounds") .sectionTitleStyle() @@ -67,8 +68,8 @@ struct NoiseView: View { .centered() } } - .contentPadding(horizontal: UIConstants.Spacing.large) - .padding(.top, UIConstants.Spacing.large) + .contentPadding(horizontal: Design.Spacing.large) + .padding(.top, Design.Spacing.large) .background(Color(.systemBackground)) // Scrollable sound selection @@ -77,15 +78,15 @@ struct NoiseView: View { sounds: viewModel.availableSounds, selectedSound: $selectedSound ) - .contentPadding(horizontal: UIConstants.Spacing.large, vertical: UIConstants.Spacing.large) + .contentPadding(horizontal: Design.Spacing.large, vertical: Design.Spacing.large) } } } private var landscapeLayout: some View { - HStack(spacing: UIConstants.Spacing.large) { + HStack(spacing: Design.Spacing.large) { // Left side: Player controls - VStack(alignment: .leading, spacing: UIConstants.Spacing.medium) { + VStack(alignment: .leading, spacing: Design.Spacing.medium) { Text("Ambient Sounds") .sectionTitleStyle() @@ -93,7 +94,7 @@ struct NoiseView: View { soundControlView } else { // Placeholder when no sound selected - VStack(spacing: UIConstants.Spacing.small) { + VStack(spacing: Design.Spacing.small) { Image(systemName: "music.note") .font(.largeTitle) .foregroundColor(.secondary) @@ -103,14 +104,14 @@ struct NoiseView: View { .foregroundColor(.secondary) } .frame(maxWidth: .infinity) - .padding(.vertical, UIConstants.Spacing.large) + .padding(.vertical, Design.Spacing.large) } Spacer() } .frame(maxWidth: 400) // Reasonable width for player section - .contentPadding(horizontal: UIConstants.Spacing.large) - .padding(.top, UIConstants.Spacing.large) + .contentPadding(horizontal: Design.Spacing.large) + .padding(.top, Design.Spacing.large) // Right side: Sound selection VStack(spacing: 0) { @@ -119,11 +120,11 @@ struct NoiseView: View { sounds: viewModel.availableSounds, selectedSound: $selectedSound ) - .contentPadding(horizontal: UIConstants.Spacing.large, vertical: UIConstants.Spacing.large) + .contentPadding(horizontal: Design.Spacing.large, vertical: Design.Spacing.large) } } } - .contentPadding(horizontal: UIConstants.Spacing.medium) + .contentPadding(horizontal: Design.Spacing.medium) } } diff --git a/TheNoiseClock/Shared/Design/BrandingConfig.swift b/TheNoiseClock/Shared/Design/BrandingConfig.swift index 4911362..fdcb4f8 100644 --- a/TheNoiseClock/Shared/Design/BrandingConfig.swift +++ b/TheNoiseClock/Shared/Design/BrandingConfig.swift @@ -12,9 +12,9 @@ import Bedrock extension Color { enum Branding { - static let primary = Color(red: 0.08, green: 0.10, blue: 0.16) - static let secondary = Color(red: 0.03, green: 0.05, blue: 0.10) - static let accent = Color.white + static let primary = AppSurface.primary + static let secondary = AppSurface.overlay + static let accent = AppTextColors.primary } } diff --git a/TheNoiseClock/Shared/Design/Design+NoiseClock.swift b/TheNoiseClock/Shared/Design/Design+NoiseClock.swift new file mode 100644 index 0000000..c8e4f6d --- /dev/null +++ b/TheNoiseClock/Shared/Design/Design+NoiseClock.swift @@ -0,0 +1,13 @@ +// +// Design+NoiseClock.swift +// TheNoiseClock +// +// Created by Matt Bruce on 1/31/26. +// + +import SwiftUI +import Bedrock + +extension Design.CornerRadius { + static let appLarge: CGFloat = 16 +} diff --git a/TheNoiseClock/Shared/Theme/NoiseClockTheme.swift b/TheNoiseClock/Shared/Design/NoiseClockTheme.swift similarity index 100% rename from TheNoiseClock/Shared/Theme/NoiseClockTheme.swift rename to TheNoiseClock/Shared/Design/NoiseClockTheme.swift diff --git a/TheNoiseClock/Shared/Design/UIConstants+Animation.swift b/TheNoiseClock/Shared/Design/UIConstants+Animation.swift deleted file mode 100644 index 2668eb3..0000000 --- a/TheNoiseClock/Shared/Design/UIConstants+Animation.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// UIConstants+Animation.swift -// TheNoiseClock -// -// Created by Matt Bruce on 1/31/26. -// - -import SwiftUI -import Bedrock - -extension UIConstants { - - // MARK: - Animation Curves - enum AnimationCurves { - static let smooth = Animation.smooth(duration: Design.Animation.standard) - static let bouncy = Animation.spring(duration: Design.Animation.springDuration, bounce: Design.Animation.springBounce) - static let quick = Animation.easeInOut(duration: Design.Animation.quick) - } -} diff --git a/TheNoiseClock/Shared/Design/UIConstants+Colors.swift b/TheNoiseClock/Shared/Design/UIConstants+Colors.swift deleted file mode 100644 index aea8680..0000000 --- a/TheNoiseClock/Shared/Design/UIConstants+Colors.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// UIConstants+Colors.swift -// TheNoiseClock -// -// Created by Matt Bruce on 1/31/26. -// - -import SwiftUI - -extension UIConstants { - - // MARK: - Colors - enum Colors { - static let accentColor = AppAccent.primary - static let primaryText = AppTextColors.primary - static let secondaryText = AppTextColors.secondary - static let background = AppSurface.primary - static let overlayBackground = AppSurface.overlay - static let overlayBorder = AppBorder.subtle - } -} diff --git a/TheNoiseClock/Shared/Design/UIConstants+Layout.swift b/TheNoiseClock/Shared/Design/UIConstants+Layout.swift deleted file mode 100644 index 67060bb..0000000 --- a/TheNoiseClock/Shared/Design/UIConstants+Layout.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// UIConstants+Layout.swift -// TheNoiseClock -// -// Created by Matt Bruce on 1/31/26. -// - -import SwiftUI -import Bedrock - -extension UIConstants { - - // MARK: - Spacing - enum Spacing { - static let extraSmall: CGFloat = Design.Spacing.xSmall - static let small: CGFloat = Design.Spacing.small - static let medium: CGFloat = Design.Spacing.medium - static let large: CGFloat = Design.Spacing.large - static let extraLarge: CGFloat = Design.Spacing.xLarge - static let huge: CGFloat = Design.Spacing.xxLarge - } - - // MARK: - Corner Radius - enum CornerRadius { - static let small: CGFloat = Design.CornerRadius.small - static let medium: CGFloat = Design.CornerRadius.large - static let large: CGFloat = 16 - static let extraLarge: CGFloat = Design.CornerRadius.xxLarge - } - - // MARK: - Border Width - enum BorderWidth { - static let thin: CGFloat = Design.LineWidth.hairline - static let normal: CGFloat = Design.LineWidth.thin - static let thick: CGFloat = Design.LineWidth.standard - } - - // MARK: - Opacity - enum Opacity { - static let disabled: Double = Design.Opacity.light - static let secondary: Double = Design.Opacity.accent - static let primary: Double = Design.Opacity.heavy - static let full: Double = 1.0 - } -} diff --git a/TheNoiseClock/Shared/Design/UIConstants.swift b/TheNoiseClock/Shared/Design/UIConstants.swift deleted file mode 100644 index d8953dd..0000000 --- a/TheNoiseClock/Shared/Design/UIConstants.swift +++ /dev/null @@ -1,11 +0,0 @@ -// -// UIConstants.swift -// TheNoiseClock -// -// Created by Matt Bruce on 9/7/25. -// - -import SwiftUI - -/// UI-specific constants for styling and layout. -enum UIConstants {} diff --git a/TheNoiseClock/Shared/Extensions/View+Extensions.swift b/TheNoiseClock/Shared/Extensions/View+Extensions.swift index cda4354..6d7b45b 100644 --- a/TheNoiseClock/Shared/Extensions/View+Extensions.swift +++ b/TheNoiseClock/Shared/Extensions/View+Extensions.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Bedrock extension View { @@ -13,10 +14,10 @@ extension View { /// - Returns: View with card styling applied func cardStyle() -> some View { self - .background(UIConstants.Colors.overlayBackground, in: RoundedRectangle(cornerRadius: UIConstants.CornerRadius.large)) + .background(AppSurface.overlay, in: RoundedRectangle(cornerRadius: Design.CornerRadius.appLarge)) .overlay( - RoundedRectangle(cornerRadius: UIConstants.CornerRadius.large) - .stroke(UIConstants.Colors.overlayBorder, lineWidth: UIConstants.BorderWidth.normal) + RoundedRectangle(cornerRadius: Design.CornerRadius.appLarge) + .stroke(AppBorder.subtle, lineWidth: Design.LineWidth.thin) ) } @@ -25,12 +26,12 @@ extension View { /// - isEnabled: Whether the button is enabled /// - color: Button color /// - Returns: View with button styling applied - func buttonStyle(isEnabled: Bool = true, color: Color = UIConstants.Colors.accentColor) -> some View { + func buttonStyle(isEnabled: Bool = true, color: Color = AppAccent.primary) -> some View { self - .padding(UIConstants.Spacing.medium) - .background(isEnabled ? color : color.opacity(UIConstants.Opacity.disabled)) + .padding(Design.Spacing.medium) + .background(isEnabled ? color : color.opacity(Design.Opacity.light)) .foregroundColor(.white) - .cornerRadius(UIConstants.CornerRadius.small) + .cornerRadius(Design.CornerRadius.small) .disabled(!isEnabled) } @@ -67,7 +68,7 @@ extension View { func sectionTitleStyle() -> some View { self .font(.title2.weight(.bold)) - .foregroundColor(UIConstants.Colors.primaryText) + .foregroundColor(AppTextColors.primary) } /// Center content horizontally with spacers @@ -84,7 +85,7 @@ extension View { /// - Parameter title: The title text /// - Returns: View with section header styling func sectionHeader(title: String) -> some View { - VStack(alignment: .leading, spacing: UIConstants.Spacing.medium) { + VStack(alignment: .leading, spacing: Design.Spacing.medium) { Text(title) .sectionTitleStyle() self @@ -96,7 +97,7 @@ extension View { /// - horizontal: Horizontal padding amount /// - vertical: Vertical padding amount /// - Returns: View with standard padding applied - func contentPadding(horizontal: CGFloat = UIConstants.Spacing.large, vertical: CGFloat? = nil) -> some View { + func contentPadding(horizontal: CGFloat = Design.Spacing.large, vertical: CGFloat? = nil) -> some View { self .padding(.horizontal, horizontal) .padding(.vertical, vertical ?? horizontal)