Last updated: 2026-05-09
Shared SwiftUI design system for the SaneApps product family.
┌─────────────────────────────────────────────────────┐
│ BEFORE YOU SHIP, ASK: │
│ │
│ 1. Does this REDUCE fear or create it? │
│ 2. Power: Does user have control? │
│ 3. Love: Does this help people? │
│ 4. Sound Mind: Is this clear and calm? │
│ │
│ Grandma test: Would her life be better? │
│ │
│ "Not fear, but power, love, sound mind" │
│ — 2 Timothy 1:7 │
└─────────────────────────────────────────────────────┘
→ Full philosophy: ~/SaneApps/meta/Brand/NORTH_STAR.md
| Mistake | What Happened | Prevention |
|---|---|---|
| Changed color without checking apps | Broke visual consistency in 3 apps | Check all consuming apps before color changes |
| Hardcoded values | Same value in multiple places, got out of sync | Use semantic tokens, not raw hex |
| Missing preview | Component hard to test visually | Always add SwiftUI Preview |
# Build
swift build
# Test
swift test
# Open the catalog app
swift run SaneUICatalogAll files stay in ~/SaneApps/infra/SaneUI/
Check brand guidelines before adding/changing colors or typography:
~/SaneApps/meta/Brand/SaneApps-Brand-Guidelines.md
After 2 build failures → check Package.swift, verify imports
swift test must pass before pushing
swift build # Not xcodebuild
swift test # Not xcodebuild testNew components need tests. No #expect(true).
Keep component files under 500 lines. Split by responsibility.
Use the catalog and the shared docs before changing app-local settings UI:
README.mdfor current package usage and shared-surface scopeARCHITECTURE.mdfor the catalog-first source-of-truth modelSources/SaneUICatalog/SaneUICatalogApp.swiftfor the live visual catalog
SaneUI/
├── Sources/SaneUI/
│ ├── Components/ # Shared settings, menus, update, permissions, feedback
│ ├── License/ # Shared license views and Keychain storage
│ ├── Buttons.swift # Shared button treatments
│ ├── Colors.swift # Semantic colors
│ └── Backgrounds.swift # Shared backgrounds and materials
├── Sources/SaneUICatalog/ # Live source-of-truth catalog app
├── Tests/SaneUITests/ # Unit tests
└── Package.swift # Package manifest
- Extend the existing shared surface if one already exists.
- If the component is shared settings/About/license UI, update the catalog too.
- Add focused tests in
Tests/SaneUITests/. - Run
swift test. - Verify at least one consuming app if the change alters shared behavior.
Shared surfaces that should stay centralized:
- settings shell and rows:
SaneSettingsContainer,CompactSection,CompactRow - customer utility menus:
SaneStandardMenu.addCoreUtilityItems - update/install recovery:
SaneUpdateEligibility,SaneApplicationMover - support surfaces:
SaneAboutView,SaneFeedbackView,SaneAboutLicenseCatalog - permissions and startup controls:
SanePermissionGuidanceView,SaneLoginItemToggle - storage and license persistence:
SaneAppStorage,KeychainService
SaneFeedbackView is the canonical in-app report flow. Keep it explicit that nothing is sent automatically, GitHub issues are public, and sensitive logs/media should go through email. Selected media is prepared locally, not silently uploaded; when media is selected, the sheet must stay open after GitHub launches so the user can drag prepared files into the issue or paste a file-sharing link for large videos.
Diagnostics copied into GitHub issue bodies must pass through SaneDiagnosticReport.sanitizedForPublicDiagnostics(_:). The sanitizer redacts local paths, file URLs, email addresses, common token shapes, and secret-like key/value pairs from user text, logs, and settings summaries before markdown is generated.
Shared About/license/update UI is reused by direct, App Store, and Setapp builds. Before adding links or copy here, check the consuming channel:
- App Store builds must not expose GitHub Sponsors, crypto donation, external purchase, Sparkle update, or direct license-key unlock paths.
- Sparkle settings UI must stay app-local and channel-gated; SaneUI may expose update/install eligibility helpers, but not
SaneSparkleRow-style controls. - Direct-download builds may show direct purchase/update surfaces when the app config explicitly enables them.
- Review shared About/license changes in
Sources/SaneUICatalog/SaneUICatalogApp.swiftand rerun the consuming app'sSaneMaster.rb appstore_preflightbefore App Store submission.
SaneUI implements the brand and shared-surface rules defined across the SaneApps docs:
All colors come from the brand palette. Don't add arbitrary colors.
SF Pro Display for headings, SF Pro for body. Use the defined scale.
Components should embody:
- Power: User control, not extraction
- Love: Built to serve, not manipulate
- Sound Mind: Clear, calm design
After each task, rate yourself:
**Self-rating: 8/10**
✅ Added preview, ran tests
❌ Forgot to check consuming apps
| Score | Meaning |
|---|---|
| 9-10 | All guidelines followed |
| 7-8 | Minor miss |
| 5-6 | Notable gaps |
| 1-4 | Multiple violations |
SaneUI is currently used by:
| App | Import Location |
|---|---|
| SaneBar | Package.swift |
| SaneClick | project.yml generated package dependency |
| SaneClip | project.yml generated package dependency |
| SaneHosts | project.yml generated package dependency |
| SaneSales | project.yml generated package dependency |
| SaneSync | project.yml generated package dependency |
| SaneVideo | project.yml generated package dependency |
Before changing any public API, check all consumers.
| Problem | Fix |
|---|---|
| Build fails in consuming app | Check Package.swift path is correct |
| Color not showing | Verify color is public |
| Preview not working | Check #if DEBUG wrapper |