Extract design system into HADesignSystem SPM package#5007
Conversation
Introduce a local Swift package (Sources/HADesignSystem) holding the design tokens, palette, semantic colors, button styles, and the core reusable SwiftUI components, so the design system can be shared across current and future Apple apps. - New local package linked into Shared-iOS and Shared-watchOS - Shared re-exports it via `@_exported import` so existing `import Shared` consumers compile unchanged - Semantic colors (haPrimary/onSurface/track/tileBorder/ haColorBorderPrimaryQuiet) are now code-defined in the package instead of the app asset catalog, removing the consuming-app catalog dependency - CloseButton decoupled from Current (Current.isCatalyst -> compile flag) - iOS-only components guarded with #if !os(watchOS) to match the previous per-target file membership (SPM has no per-file membership) - MDI/L10n/Server-coupled components (ExternalLinkButton, LabsLabel, Modal*, SettingsButton, PrivacyNoteView, ServersPickerPillList, CollapsibleView) intentionally kept in Shared for a follow-up PR Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Found 3 unused localization strings in the codebase. Click to see detailsTo remove them, run the |
There was a problem hiding this comment.
Pull request overview
This PR extracts the app’s design-system tokens/colors/styles/components into a new local Swift Package (Sources/HADesignSystem) and re-exports it from Shared via @_exported import HADesignSystem so existing import Shared consumers can keep compiling unchanged. It also replaces several semantic asset-catalog colors with code-defined semantic colors suitable for a reusable package.
Changes:
- Introduces local SPM package
HADesignSystemcontaining tokens, semantic/base colors, styles, and reusable SwiftUI components (+ a small test target). - Re-exports
HADesignSystemfromSharedand updates a handful ofSharedfiles to import/use the new module. - Removes the migrated semantic colorsets from
Sources/Shared/Assets/Colors.xcassetsand wires the package into the Xcode project.
Reviewed changes
Copilot reviewed 28 out of 38 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| Sources/Shared/MaterialDesignIcons+CarPlay.swift | Imports HADesignSystem to use shared colors/tokens. |
| Sources/Shared/MagicItem/MagicItem.swift | Imports HADesignSystem for shared semantic color usage. |
| Sources/Shared/Environment/Style.swift | Uses HADesignSystem tokens/colors for UI styling. |
| Sources/Shared/EntityIconColorProvider.swift | Uses HADesignSystem semantic color fallback. |
| Sources/Shared/DesignSystem/HADesignSystemExports.swift | Re-exports HADesignSystem from Shared. |
| Sources/Shared/DesignSystem/Constants/Sizes.swift | Removes Sizes from Shared (moved to package). |
| Sources/Shared/DesignSystem/Components/ServersPickerPillList.swift | Imports HADesignSystem for moved symbols. |
| Sources/Shared/DesignSystem/Components/PrivacyNoteView.swift | Imports HADesignSystem for moved symbols. |
| Sources/Shared/DesignSystem/Components/ModalReusableButton.swift | Imports HADesignSystem for moved symbols (iOS-only guarded). |
| Sources/Shared/DesignSystem/Components/LabsLabel.swift | Imports HADesignSystem for moved symbols. |
| Sources/Shared/DesignSystem/Components/ExternalLinkButton.swift | Imports HADesignSystem for moved symbols. |
| Sources/Shared/Common/Extensions/Color+hex.swift | Imports HADesignSystem to reference new semantic colors. |
| Sources/Shared/Assets/Colors.xcassets/trackColor.colorset/Contents.json | Removes asset-based semantic color. |
| Sources/Shared/Assets/Colors.xcassets/onSurface.colorset/Contents.json | Removes asset-based semantic color. |
| Sources/Shared/Assets/Colors.xcassets/haPrimary.colorset/Contents.json | Removes asset-based semantic color. |
| Sources/Shared/Assets/Colors.xcassets/ha-color-border-primary-quiet.colorset/Contents.json | Removes asset-based semantic color. |
| Sources/Shared/Assets/Colors.xcassets/Border/tileBorder.colorset/Contents.json | Removes asset-based semantic color. |
| Sources/HADesignSystem/Package.swift | Defines the new local SPM package. |
| Sources/HADesignSystem/Sources/Tokens/Sizes.swift | Adds token moved from Shared. |
| Sources/HADesignSystem/Sources/Tokens/HACornerRadius.swift | Adds corner-radius token moved from Shared. |
| Sources/HADesignSystem/Sources/Tokens/DesignSystem.swift | Adds core token namespace (fonts/spaces/radii/etc). |
| Sources/HADesignSystem/Sources/Colors/Color+Semantic.swift | Implements code-defined semantic colors replacing removed colorsets. |
| Sources/HADesignSystem/Sources/Colors/Color+Hex.swift | Adds hex parsing + UIColor helpers inside the package. |
| Sources/HADesignSystem/Sources/Colors/BaseColors.swift | Adds base palette constants. |
| Sources/HADesignSystem/Sources/Styles/HAButtonStyles.swift | Adds shared button styles. |
| Sources/HADesignSystem/Sources/Styles/HAButton.swift | Adds TextButton style. |
| Sources/HADesignSystem/Sources/Extensions/View+ConditionalModifier.swift | Adds shared conditional modifier helper. |
| Sources/HADesignSystem/Sources/Extensions/UIScreen+PerfectCornerRadius.swift | Moves UIScreen corner-radius helper (watchOS-guarded). |
| Sources/HADesignSystem/Sources/Components/CardView.swift | Moves CardView component (watchOS-guarded). |
| Sources/HADesignSystem/Sources/Components/PillView.swift | Moves PillView component (watchOS-guarded). |
| Sources/HADesignSystem/Sources/Components/HAProgressView.swift | Moves HAProgressView component. |
| Sources/HADesignSystem/Sources/Components/HATextField.swift | Moves HATextField component. |
| Sources/HADesignSystem/Sources/Components/SheetCloseButton.swift | Moves SheetCloseButton component (watchOS-guarded). |
| Sources/HADesignSystem/Sources/Components/CloseButton.swift | Moves CloseButton component + decouples from Current (watchOS-guarded). |
| Sources/HADesignSystem/Sources/Components/AppleLikeBottomSheet.swift | Moves bottom sheet component (watchOS-guarded). |
| Sources/HADesignSystem/Tests/HADesignSystemTests.swift | Adds initial unit tests for tokens/colors. |
| HomeAssistant.xcodeproj/project.pbxproj | Adds the local package reference and updates build phases/groups. |
| .gitignore | Ignores package-local Package.resolved. |
Color+Semantic: route rgb components through helpers with explicit / 255.0 float division (behavior unchanged; the previous form was already float division in Double/CGFloat context, just less obvious). HADesignSystemTests: drop the Color-equality and non-optional-nil assertions; verify haPrimary by extracting its RGBA components instead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The code-defined colors were on `extension Color`, but implicit-member uses in a ShapeStyle context (e.g. HATextField's `.stroke(.tileBorder)`, and `.foregroundStyle(.haPrimary)` / `.fill(.haPrimary)` across consumers) require the accessor on `ShapeStyle where Self == Color` — which is what the old asset-catalog codegen provided. Defining them there also keeps `Color.haPrimary` resolving, so both explicit and implicit uses compile. Fixes 'type ShapeStyle has no member tileBorder' build failure. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a categorized catalog of the package's reusable components: - ComponentCategory: buttons / controls / inputs / containers / indicators - DesignSystemComponent: CaseIterable enum, each case exposes a title, category, and a @ViewBuilder preview of the live component - ComponentsLibraryView: reads the enum and renders every component grouped by category (the in-app glossary / explorer) - BottomSheetGalleryDemo: stateful present-button demo for the sheet Also documents in AGENTS.md that reusable components go in the HADesignSystem package (not the app) and must be registered in DesignSystemComponent so they appear in the explorer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-package # Conflicts: # AGENTS.md
What
Extracts the design system into a new local Swift package at
Sources/HADesignSystem, so tokens, colors, styles, and reusable components live in one portable module that can be shared with current targets (iOS, watchOS, Catalyst) and future Apple apps.This is the first, foundational slice. It is deliberately scoped to the pieces that are cleanly portable; the components with heavier coupling are called out below as follow-ups.
Why a local SPM package (not an Xcode framework target)
Package.swift, so a future app can drop it in or (later) consume it from its own git repo by URL. An Xcode framework target is bound to this.xcodeproj.Shared-iOS/Shared-watchOShandling for this code.Sources/SharedPush,Sources/PromiseKitDynamic), so the wiring is proven.Zero churn for consumers
Sharedre-exports the package via@_exported import HADesignSystem(HADesignSystemExports.swift). Every existingimport Sharedfile (≈135 across App/Extensions/CarPlay/Thread) keeps compiling unchanged. Only the ~10 files insideSharedthat reference moved symbols got an explicitimport HADesignSystem.What moved into the package
DesignSystem(spacing/font/radius/animation),Sizes,HACornerRadiusBaseColorspalette,Color+Hex, newColor+SemanticView.modify,UIScreen.displayCornerRadiusHAButtonStyles(allHA*ButtonStyle),TextButtonCardView,PillView,HAProgressView,HATextField,SheetCloseButton,CloseButton,AppleLikeBottomSheetKey decisions
Color.haPrimary,.onSurface,.track,.tileBorder,.haColorBorderPrimaryQuiet, plusUIColor.haPrimary) using the exact values from the old colorsets. A shared package can't depend on the consuming app's asset catalog, so the 5 colorsets were removed fromColors.xcassetsand reimplemented in Color+Semantic.swift. Adaptive (light/dark) colors use aUIColordynamic provider on iOS/Catalyst and fall back to the dark value on watchOS. The remaining Background/Domain colorsets are untouched.CloseButtondecoupled fromCurrent:Current.isCatalyst→#if targetEnvironment(macCatalyst).CardView,PillView,SheetCloseButton,CloseButton,AppleLikeBottomSheet,UIScreen+PerfectCornerRadius) are wrapped in#if !os(watchOS)to match the previousShared-watchOSmembership exactly.DesignSystem,HAButtonStyles,HATextField,HAProgressViewstay cross-platform as before.Sizes.maxWidthForLargerScreensis nowpublic(it was implicitly available before only via direct App-target file membership).Intentionally left in
Shared(follow-up PR)These pull in
MaterialDesignIcons,L10nproduct copy, orCurrent/Server, which need dedicated abstractions before they can move:ExternalLinkButton/ActionLinkButton,LabsLabel,ModalReusableButton/ModalCloseButton,SettingsButton,PrivacyNoteView,ServersPickerPillList,CollapsibleView.Verification (needs a local build — not run here)
Shared-iOSandShared-watchOS).@_exportedmakesDesignSystem.*,Color.haPrimary, button styles, and the moved components visible toimport Sharedconsumers without new imports.haPrimaryRGBA components) run on an iOS simulator destination via Xcode. The package targets iOS/watchOS and uses UIKit, so macOS CLIswift testis not supported (unlike the pure-modelSharedPushpackage).Lint (SwiftFormat + SwiftLint + Rubocop) passes locally.
🤖 Generated with Claude Code