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

This commit is contained in:
Matt Bruce 2026-01-12 13:29:00 -06:00
parent 4a420242a8
commit ce905431ed
5 changed files with 88 additions and 14 deletions

View File

@ -39,6 +39,8 @@ Use this checklist when setting up a new app with Bedrock:
- [ ] Define custom `SurfaceColorProvider` with your brand colors - [ ] Define custom `SurfaceColorProvider` with your brand colors
- [ ] Define custom `AccentColorProvider` for interactive elements - [ ] Define custom `AccentColorProvider` for interactive elements
- [ ] Create typealiases for easy access (e.g., `AppSurface`, `AppAccent`) - [ ] Create typealiases for easy access (e.g., `AppSurface`, `AppAccent`)
- [ ] **Import Bedrock**: Add `import Bedrock` to all files using theme/design constants (Required)
- [ ] **Asset Catalog Format**: Ensure `Contents.json` uses the `"color"` key and `"luminosity"` appearance (See `THEME_GUIDE.md` for template)
- [ ] **Legibility Alignment**: Apply `.preferredColorScheme(.dark)` to the root view if using a dark theme to ensure system text resolves to white (Required for legibility) - [ ] **Legibility Alignment**: Apply `.preferredColorScheme(.dark)` to the root view if using a dark theme to ensure system text resolves to white (Required for legibility)
- [ ] Apply theme colors to all views (never use hardcoded colors) - [ ] Apply theme colors to all views (never use hardcoded colors)
@ -98,6 +100,7 @@ Then add it to your target:
Use the `Design` enum for consistent spacing, sizing, and styling: Use the `Design` enum for consistent spacing, sizing, and styling:
```swift ```swift
import SwiftUI
import Bedrock import Bedrock
struct MyView: View { struct MyView: View {

View File

@ -59,20 +59,33 @@ public struct AppIconView: View {
// Size calculations // Size calculations
private var iconSize: CGFloat { size * 0.35 } private var iconSize: CGFloat { size * 0.35 }
private var subtitleSize: CGFloat { size * 0.25 }
/// Dynamic subtitle size based on text length.
private var subtitleSize: CGFloat {
guard let length = config.subtitle?.count else { return 0 }
let baseSize = size * 0.14 // Reduced from 0.25 to prevent overwhelm
let scaleFactor: CGFloat = switch length {
case ...4: 1.0 // "PRO", "PLUS"
case 5...6: 0.85 // "SAMPLE", "CAMERA"
case 7...9: 0.70 // "MESSENGER"
default: 0.55
}
return baseSize * scaleFactor
}
/// Dynamic title size based on text length. /// Dynamic title size based on text length.
/// Shorter titles get larger fonts, longer titles shrink to fit within the border. /// Shorter titles get larger fonts, longer titles shrink to fit within the border.
private var titleSize: CGFloat { private var titleSize: CGFloat {
let baseSize = size * 0.12 let baseSize = size * 0.10 // Adjusted to 0.10 to better balance with subtitle
let length = config.title.count let length = config.title.count
// Scale factor: full size for 6 chars, progressively smaller for longer // Scale factor: full size for 6 chars, progressively smaller for longer
let scaleFactor: CGFloat = switch length { let scaleFactor: CGFloat = switch length {
case ...6: 1.0 // "SELFIE", "CAMERA" case ...6: 1.0 // "BEDROCK", "SELFIE"
case 7: 0.95 // "WEATHER" case 7...8: 0.90 // "SETTINGS"
case 8: 0.85 // "SETTINGS" case 9...10: 0.75 // "MESSENGER"
case 9: 0.75 // "MESSENGER"
default: 0.65 // Very long titles default: 0.65 // Very long titles
} }

View File

@ -465,15 +465,12 @@ extension LaunchScreenConfig {
cornerSymbol: "sparkle", // Sparkles in corners cornerSymbol: "sparkle", // Sparkles in corners
decorativeSymbol: "circle.fill", // Circle in decorative line decorativeSymbol: "circle.fill", // Circle in decorative line
patternStyle: .radial, // Radial glow effect patternStyle: .radial, // Radial glow effect
layoutStyle: .iconAboveTitle, iconSpacing: 12,
animationDuration: 0.6,
primaryColor: Color.Branding.primary, primaryColor: Color.Branding.primary,
secondaryColor: Color.Branding.secondary, secondaryColor: Color.Branding.secondary,
accentColor: Color.Branding.accent, accentColor: Color.Branding.accent,
titleColor: .white, titleColor: .white
iconSize: 52,
titleSize: 38,
iconSpacing: 12,
animationDuration: 0.6
) )
} }
``` ```

View File

@ -389,6 +389,67 @@ For dark themes:
- Text is white with varying opacity - Text is white with varying opacity
- Ensure sufficient contrast (WCAG AA: 4.5:1 for text) - Ensure sufficient contrast (WCAG AA: 4.5:1 for text)
## Asset Catalog Best Practices
While you can define colors directly in Swift, the **gold standard** for Bedrock themes is using an **Asset Catalog (`.xcassets`)**. This allows for visual editing, better system integration, and automatic Light/Dark mode switching.
### 1. Mandatory JSON Structure (Contents.json)
Xcode is extremely strict about the JSON schema in `.colorset/Contents.json`. To ensure your colors are correctly recognized (avoiding "unassigned children"), use this exact pattern:
> [!IMPORTANT]
> Use the `"color"` key for data (not `"value"`) and `"appearance": "luminosity"` for theme variants.
```json
{
"colors" : [
{
"idiom" : "universal",
"color" : {
"color-space" : "display-p3",
"components" : { "alpha": "1.000", "red": "0.06", "green": "0.06", "blue": "0.09" }
}
},
{
"appearances" : [ { "appearance": "luminosity", "value": "light" } ],
"idiom" : "universal",
"color" : {
"color-space" : "display-p3",
"components" : { "alpha": "1.000", "red": "0.96", "green": "0.96", "blue": "0.98" }
}
},
{
"appearances" : [ { "appearance": "luminosity", "value": "dark" } ],
"idiom" : "universal",
"color" : {
"color-space" : "display-p3",
"components" : { "alpha": "1.000", "red": "0.06", "green": "0.06", "blue": "0.09" }
}
}
],
"info" : { "author" : "xcode", "version" : 1 }
}
```
### 2. The 3-Variant Pattern
Always provide **Any (Universal)**, **Light**, and **Dark** variants.
- **Any**: Typically matches your dark branding for high-end apps.
- **Light/Dark**: Explicit luminosity overrides for standard OS behavior.
### 3. Folder Namespacing
Organize colors into folders (`Surface/`, `Text/`, `Accent/`).
- Enable **"Provides Namespace"** in the folder's `Contents.json`.
- This allows you to reference colors cleanly as `Color("Surface/Primary")` while keeping the catalog organized.
```json
{
"info" : { "author" : "xcode", "version" : 1 },
"properties" : { "provides-namespace" : true }
}
```
## Migrating Existing Views ## Migrating Existing Views
If you have views using hardcoded colors, migrate them: If you have views using hardcoded colors, migrate them:

View File

@ -150,7 +150,7 @@ typealias AppBorder = MyAppBorderColors
typealias AppInteractive = MyAppInteractiveColors typealias AppInteractive = MyAppInteractiveColors
``` ```
> **Important**: Do NOT add typealiases inside `extension Color { }` as they will conflict with Bedrock's defaults and cause "ambiguous use" compiler errors. Use top-level `App`-prefixed typealiases instead. > **Important**: Do NOT add typealiases inside `extension Color { }` as they will conflict with Bedrock's defaults and cause "ambiguous use" compiler errors. Use top-level `App`-prefixed typealiases instead. Ensure ALL typealiases mentioned in the theme are created, as components like `SettingsToggle` or `SettingsCard` rely on them for consistent branding.
--- ---
@ -284,7 +284,7 @@ SettingsSectionHeader(
### SettingsToggle ### SettingsToggle
A toggle row with title, subtitle, and optional title accessory (e.g., premium crown). A toggle row with title, subtitle, and optional title accessory (e.g., premium crown). **Note: Subtitle is required.**
```swift ```swift
// Basic toggle // Basic toggle