Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2026-01-31 11:05:55 -06:00
parent a3398f0dd0
commit c9b1b3cf62
9 changed files with 114 additions and 83 deletions

7
PRD.md
View File

@ -373,10 +373,13 @@ TheNoiseClock/
│ │ ├── Debug.xcconfig # Debug settings
│ │ └── Release.xcconfig # Release settings
│ ├── Shared/
│ │ ├── BrandingConfig.swift # Bedrock branding + launch config
│ │ ├── Design/
│ │ │ ├── BrandingConfig.swift # Bedrock branding + launch config
│ │ │ ├── AppConstants.swift # App-wide constants and configuration
│ │ │ ├── UIConstants.swift # UI-specific constants (colors, sizes, etc.)
│ │ │ ├── UIConstants.swift # UI constants namespace
│ │ │ ├── UIConstants+Colors.swift # Theme-backed colors
│ │ │ ├── UIConstants+Layout.swift # Spacing/radius/opacity
│ │ │ ├── UIConstants+Animation.swift # Animation curves
│ │ │ └── Fonts/
│ │ │ ├── Font.Design.swift
│ │ │ ├── Font.Weight.swift

View File

@ -87,7 +87,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`
- Branding config: `TheNoiseClock/Shared/BrandingConfig.swift`
- Branding config: `TheNoiseClock/Shared/Design/BrandingConfig.swift`
- Launch screen: `TheNoiseClock/Resources/LaunchScreen.storyboard`
To generate an app icon in DEBUG:

View File

@ -32,9 +32,8 @@
EA384AFB2E6E6B6000CA7D50 /* TheNoiseClock.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TheNoiseClock.app; sourceTree = BUILT_PRODUCTS_DIR; };
EA384B082E6E6B6100CA7D50 /* TheNoiseClockTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TheNoiseClockTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
EA384B122E6E6B6100CA7D50 /* TheNoiseClockUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TheNoiseClockUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
EAD6E3AE5A7F4D3DB37CF6D1 /* Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = TheNoiseClock/Configuration/Base.xcconfig; sourceTree = SOURCE_ROOT; };
EAD6E3AF5A7F4D3DB37CF6D1 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = TheNoiseClock/Configuration/Debug.xcconfig; sourceTree = SOURCE_ROOT; };
EAD6E3B05A7F4D3DB37CF6D1 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = TheNoiseClock/Configuration/Release.xcconfig; sourceTree = SOURCE_ROOT; };
EAD6E3AF5A7F4D3DB37CF6D1 /* TheNoiseClock/Configuration/Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = TheNoiseClock/Configuration/Debug.xcconfig; sourceTree = SOURCE_ROOT; };
EAD6E3B05A7F4D3DB37CF6D1 /* TheNoiseClock/Configuration/Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = TheNoiseClock/Configuration/Release.xcconfig; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
@ -102,6 +101,7 @@
EA384B0B2E6E6B6100CA7D50 /* TheNoiseClockTests */,
EA384B152E6E6B6100CA7D50 /* TheNoiseClockUITests */,
EA384AFC2E6E6B6000CA7D50 /* Products */,
EAC057642F2E69E8007F87EA /* Recovered References */,
);
sourceTree = "<group>";
};
@ -115,6 +115,15 @@
name = Products;
sourceTree = "<group>";
};
EAC057642F2E69E8007F87EA /* Recovered References */ = {
isa = PBXGroup;
children = (
EAD6E3AF5A7F4D3DB37CF6D1 /* TheNoiseClock/Configuration/Debug.xcconfig */,
EAD6E3B05A7F4D3DB37CF6D1 /* TheNoiseClock/Configuration/Release.xcconfig */,
);
name = "Recovered References";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -300,7 +309,7 @@
/* Begin XCBuildConfiguration section */
EA384B1A2E6E6B6100CA7D50 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = EAD6E3AF5A7F4D3DB37CF6D1 /* Debug.xcconfig */;
baseConfigurationReference = EAD6E3AF5A7F4D3DB37CF6D1 /* TheNoiseClock/Configuration/Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
@ -365,7 +374,7 @@
};
EA384B1B2E6E6B6100CA7D50 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = EAD6E3B05A7F4D3DB37CF6D1 /* Release.xcconfig */;
baseConfigurationReference = EAD6E3B05A7F4D3DB37CF6D1 /* TheNoiseClock/Configuration/Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
@ -423,7 +432,7 @@
};
EA384B1D2E6E6B6100CA7D50 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = EAD6E3AF5A7F4D3DB37CF6D1 /* Debug.xcconfig */;
baseConfigurationReference = EAD6E3AF5A7F4D3DB37CF6D1 /* TheNoiseClock/Configuration/Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
@ -457,7 +466,7 @@
};
EA384B1E2E6E6B6100CA7D50 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = EAD6E3B05A7F4D3DB37CF6D1 /* Release.xcconfig */;
baseConfigurationReference = EAD6E3B05A7F4D3DB37CF6D1 /* TheNoiseClock/Configuration/Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
@ -491,7 +500,7 @@
};
EA384B202E6E6B6100CA7D50 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = EAD6E3AF5A7F4D3DB37CF6D1 /* Debug.xcconfig */;
baseConfigurationReference = EAD6E3AF5A7F4D3DB37CF6D1 /* TheNoiseClock/Configuration/Debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
@ -514,7 +523,7 @@
};
EA384B212E6E6B6100CA7D50 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = EAD6E3B05A7F4D3DB37CF6D1 /* Release.xcconfig */;
baseConfigurationReference = EAD6E3B05A7F4D3DB37CF6D1 /* TheNoiseClock/Configuration/Release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
@ -537,7 +546,7 @@
};
EA384B232E6E6B6100CA7D50 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = EAD6E3AF5A7F4D3DB37CF6D1 /* Debug.xcconfig */;
baseConfigurationReference = EAD6E3AF5A7F4D3DB37CF6D1 /* TheNoiseClock/Configuration/Debug.xcconfig */;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
@ -558,7 +567,7 @@
};
EA384B242E6E6B6100CA7D50 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = EAD6E3B05A7F4D3DB37CF6D1 /* Release.xcconfig */;
baseConfigurationReference = EAD6E3B05A7F4D3DB37CF6D1 /* TheNoiseClock/Configuration/Release.xcconfig */;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;

View File

@ -7,7 +7,7 @@
<key>TheNoiseClock.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
<integer>0</integer>
</dict>
</dict>
</dict>

View File

@ -0,0 +1,19 @@
//
// 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)
}
}

View File

@ -0,0 +1,21 @@
//
// 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
}
}

View File

@ -0,0 +1,45 @@
//
// 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
}
}

View File

@ -7,71 +7,5 @@
import SwiftUI
/// UI-specific constants for colors, sizes, and styling
enum UIConstants {
// MARK: - Colors
enum Colors {
static let accentColor = Color.blue
static let primaryText = Color.white
static let secondaryText = Color.gray
static let background = Color.black
static let overlayBackground = Color.black.opacity(0.25)
static let overlayBorder = Color.white.opacity(0.05)
}
// MARK: - Font Sizes
enum FontSizes {
static let largeTitle: CGFloat = 34
static let title: CGFloat = 28
static let title2: CGFloat = 22
static let title3: CGFloat = 20
static let headline: CGFloat = 17
static let body: CGFloat = 17
static let callout: CGFloat = 16
static let subheadline: CGFloat = 15
static let footnote: CGFloat = 13
static let caption: CGFloat = 12
static let caption2: CGFloat = 11
}
// MARK: - Spacing
enum Spacing {
static let extraSmall: CGFloat = 4
static let small: CGFloat = 8
static let medium: CGFloat = 12
static let large: CGFloat = 16
static let extraLarge: CGFloat = 20
static let huge: CGFloat = 24
}
// MARK: - Corner Radius
enum CornerRadius {
static let small: CGFloat = 8
static let medium: CGFloat = 12
static let large: CGFloat = 16
static let extraLarge: CGFloat = 20
}
// MARK: - Border Width
enum BorderWidth {
static let thin: CGFloat = 0.5
static let normal: CGFloat = 1
static let thick: CGFloat = 2
}
// MARK: - Opacity
enum Opacity {
static let disabled: Double = 0.3
static let secondary: Double = 0.6
static let primary: Double = 0.8
static let full: Double = 1.0
}
// MARK: - Animation Curves
enum AnimationCurves {
static let smooth = Animation.smooth(duration: AppConstants.AnimationDurations.medium)
static let bouncy = Animation.bouncy(duration: AppConstants.AnimationDurations.bouncy)
static let quick = Animation.easeInOut(duration: AppConstants.AnimationDurations.short)
}
}
/// UI-specific constants for styling and layout.
enum UIConstants {}