diff --git a/BusinessCard/Assets.xcassets/AppAccentGold.colorset/Contents.json b/BusinessCard/Assets.xcassets/AppAccentGold.colorset/Contents.json new file mode 100644 index 0000000..cb7dc1e --- /dev/null +++ b/BusinessCard/Assets.xcassets/AppAccentGold.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.250", + "green" : "0.750", + "red" : "0.950" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/AppAccentInk.colorset/Contents.json b/BusinessCard/Assets.xcassets/AppAccentInk.colorset/Contents.json new file mode 100644 index 0000000..52f0405 --- /dev/null +++ b/BusinessCard/Assets.xcassets/AppAccentInk.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.140", + "green" : "0.120", + "red" : "0.120" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/AppAccentMint.colorset/Contents.json b/BusinessCard/Assets.xcassets/AppAccentMint.colorset/Contents.json new file mode 100644 index 0000000..fc74b20 --- /dev/null +++ b/BusinessCard/Assets.xcassets/AppAccentMint.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.550", + "green" : "0.650", + "red" : "0.200" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/AppAccentRed.colorset/Contents.json b/BusinessCard/Assets.xcassets/AppAccentRed.colorset/Contents.json new file mode 100644 index 0000000..60b6b65 --- /dev/null +++ b/BusinessCard/Assets.xcassets/AppAccentRed.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.280", + "green" : "0.330", + "red" : "0.950" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/AppAccentSlate.colorset/Contents.json b/BusinessCard/Assets.xcassets/AppAccentSlate.colorset/Contents.json new file mode 100644 index 0000000..162035d --- /dev/null +++ b/BusinessCard/Assets.xcassets/AppAccentSlate.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.400", + "green" : "0.330", + "red" : "0.290" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/BadgeNeutral.colorset/Contents.json b/BusinessCard/Assets.xcassets/BadgeNeutral.colorset/Contents.json new file mode 100644 index 0000000..73272dd --- /dev/null +++ b/BusinessCard/Assets.xcassets/BadgeNeutral.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.900", + "green" : "0.890", + "red" : "0.890" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/BadgeStar.colorset/Contents.json b/BusinessCard/Assets.xcassets/BadgeStar.colorset/Contents.json new file mode 100644 index 0000000..f1c82e7 --- /dev/null +++ b/BusinessCard/Assets.xcassets/BadgeStar.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.340", + "green" : "0.820", + "red" : "0.980" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/BrandingAccent.colorset/Contents.json b/BusinessCard/Assets.xcassets/BrandingAccent.colorset/Contents.json new file mode 100644 index 0000000..97650a1 --- /dev/null +++ b/BusinessCard/Assets.xcassets/BrandingAccent.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/BrandingPrimary.colorset/Contents.json b/BusinessCard/Assets.xcassets/BrandingPrimary.colorset/Contents.json new file mode 100644 index 0000000..4862a99 --- /dev/null +++ b/BusinessCard/Assets.xcassets/BrandingPrimary.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.278", + "green" : "0.329", + "red" : "0.949" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/BrandingSecondary.colorset/Contents.json b/BusinessCard/Assets.xcassets/BrandingSecondary.colorset/Contents.json new file mode 100644 index 0000000..b7cd0ba --- /dev/null +++ b/BusinessCard/Assets.xcassets/BrandingSecondary.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.150", + "green" : "0.180", + "red" : "0.650" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/CardPaletteAmber.colorset/Contents.json b/BusinessCard/Assets.xcassets/CardPaletteAmber.colorset/Contents.json new file mode 100644 index 0000000..871ae27 --- /dev/null +++ b/BusinessCard/Assets.xcassets/CardPaletteAmber.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.280", + "green" : "0.750", + "red" : "0.980" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/CardPaletteCoral.colorset/Contents.json b/BusinessCard/Assets.xcassets/CardPaletteCoral.colorset/Contents.json new file mode 100644 index 0000000..54cd700 --- /dev/null +++ b/BusinessCard/Assets.xcassets/CardPaletteCoral.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.330", + "green" : "0.350", + "red" : "0.950" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/CardPaletteForest.colorset/Contents.json b/BusinessCard/Assets.xcassets/CardPaletteForest.colorset/Contents.json new file mode 100644 index 0000000..3531ad9 --- /dev/null +++ b/BusinessCard/Assets.xcassets/CardPaletteForest.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.310", + "green" : "0.370", + "red" : "0.130" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/CardPaletteLime.colorset/Contents.json b/BusinessCard/Assets.xcassets/CardPaletteLime.colorset/Contents.json new file mode 100644 index 0000000..432ac26 --- /dev/null +++ b/BusinessCard/Assets.xcassets/CardPaletteLime.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.340", + "green" : "0.820", + "red" : "0.730" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/CardPaletteMidnight.colorset/Contents.json b/BusinessCard/Assets.xcassets/CardPaletteMidnight.colorset/Contents.json new file mode 100644 index 0000000..2ef13f1 --- /dev/null +++ b/BusinessCard/Assets.xcassets/CardPaletteMidnight.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.220", + "green" : "0.160", + "red" : "0.120" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/CardPaletteOcean.colorset/Contents.json b/BusinessCard/Assets.xcassets/CardPaletteOcean.colorset/Contents.json new file mode 100644 index 0000000..8896122 --- /dev/null +++ b/BusinessCard/Assets.xcassets/CardPaletteOcean.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.560", + "green" : "0.450", + "red" : "0.080" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/CardPalettePlum.colorset/Contents.json b/BusinessCard/Assets.xcassets/CardPalettePlum.colorset/Contents.json new file mode 100644 index 0000000..47d0750 --- /dev/null +++ b/BusinessCard/Assets.xcassets/CardPalettePlum.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.520", + "green" : "0.270", + "red" : "0.560" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/CardPaletteRose.colorset/Contents.json b/BusinessCard/Assets.xcassets/CardPaletteRose.colorset/Contents.json new file mode 100644 index 0000000..b0898a1 --- /dev/null +++ b/BusinessCard/Assets.xcassets/CardPaletteRose.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.730", + "green" : "0.680", + "red" : "0.950" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/CardPaletteSand.colorset/Contents.json b/BusinessCard/Assets.xcassets/CardPaletteSand.colorset/Contents.json new file mode 100644 index 0000000..372cb6b --- /dev/null +++ b/BusinessCard/Assets.xcassets/CardPaletteSand.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.680", + "green" : "0.830", + "red" : "0.930" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/CardPaletteSlate.colorset/Contents.json b/BusinessCard/Assets.xcassets/CardPaletteSlate.colorset/Contents.json new file mode 100644 index 0000000..aed5f53 --- /dev/null +++ b/BusinessCard/Assets.xcassets/CardPaletteSlate.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.500", + "green" : "0.440", + "red" : "0.380" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/CardPaletteViolet.colorset/Contents.json b/BusinessCard/Assets.xcassets/CardPaletteViolet.colorset/Contents.json new file mode 100644 index 0000000..f574f0f --- /dev/null +++ b/BusinessCard/Assets.xcassets/CardPaletteViolet.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.620", + "green" : "0.360", + "red" : "0.420" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/SocialCashApp.colorset/Contents.json b/BusinessCard/Assets.xcassets/SocialCashApp.colorset/Contents.json new file mode 100644 index 0000000..a869903 --- /dev/null +++ b/BusinessCard/Assets.xcassets/SocialCashApp.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.350", + "green" : "0.820", + "red" : "0.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/SocialFacebook.colorset/Contents.json b/BusinessCard/Assets.xcassets/SocialFacebook.colorset/Contents.json new file mode 100644 index 0000000..1dec2ff --- /dev/null +++ b/BusinessCard/Assets.xcassets/SocialFacebook.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.700", + "green" : "0.400", + "red" : "0.260" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/SocialGitHub.colorset/Contents.json b/BusinessCard/Assets.xcassets/SocialGitHub.colorset/Contents.json new file mode 100644 index 0000000..8671ad4 --- /dev/null +++ b/BusinessCard/Assets.xcassets/SocialGitHub.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.130", + "green" : "0.130", + "red" : "0.130" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/SocialInstagram.colorset/Contents.json b/BusinessCard/Assets.xcassets/SocialInstagram.colorset/Contents.json new file mode 100644 index 0000000..f60f6c7 --- /dev/null +++ b/BusinessCard/Assets.xcassets/SocialInstagram.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.420", + "green" : "0.190", + "red" : "0.880" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/SocialLinkedIn.colorset/Contents.json b/BusinessCard/Assets.xcassets/SocialLinkedIn.colorset/Contents.json new file mode 100644 index 0000000..26218b4 --- /dev/null +++ b/BusinessCard/Assets.xcassets/SocialLinkedIn.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.710", + "green" : "0.470", + "red" : "0.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/SocialTelegram.colorset/Contents.json b/BusinessCard/Assets.xcassets/SocialTelegram.colorset/Contents.json new file mode 100644 index 0000000..1d2f9c6 --- /dev/null +++ b/BusinessCard/Assets.xcassets/SocialTelegram.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.890", + "green" : "0.630", + "red" : "0.160" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/SocialThreads.colorset/Contents.json b/BusinessCard/Assets.xcassets/SocialThreads.colorset/Contents.json new file mode 100644 index 0000000..8ec08b9 --- /dev/null +++ b/BusinessCard/Assets.xcassets/SocialThreads.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.000", + "green" : "0.000", + "red" : "0.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/SocialTikTok.colorset/Contents.json b/BusinessCard/Assets.xcassets/SocialTikTok.colorset/Contents.json new file mode 100644 index 0000000..8ec08b9 --- /dev/null +++ b/BusinessCard/Assets.xcassets/SocialTikTok.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.000", + "green" : "0.000", + "red" : "0.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/SocialTwitter.colorset/Contents.json b/BusinessCard/Assets.xcassets/SocialTwitter.colorset/Contents.json new file mode 100644 index 0000000..29cfd34 --- /dev/null +++ b/BusinessCard/Assets.xcassets/SocialTwitter.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.950", + "green" : "0.630", + "red" : "0.110" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/SocialVenmo.colorset/Contents.json b/BusinessCard/Assets.xcassets/SocialVenmo.colorset/Contents.json new file mode 100644 index 0000000..67ab6a6 --- /dev/null +++ b/BusinessCard/Assets.xcassets/SocialVenmo.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.790", + "green" : "0.530", + "red" : "0.220" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Assets.xcassets/SocialWhatsApp.colorset/Contents.json b/BusinessCard/Assets.xcassets/SocialWhatsApp.colorset/Contents.json new file mode 100644 index 0000000..a93bdc4 --- /dev/null +++ b/BusinessCard/Assets.xcassets/SocialWhatsApp.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.380", + "green" : "0.680", + "red" : "0.150" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BusinessCard/Design/BrandingConfig.swift b/BusinessCard/Design/BrandingConfig.swift index 1edbabb..b204176 100644 --- a/BusinessCard/Design/BrandingConfig.swift +++ b/BusinessCard/Design/BrandingConfig.swift @@ -15,13 +15,13 @@ extension Color { enum Branding { /// Primary gradient color (warm coral/red). /// Must match LaunchBackground.colorset exactly to prevent flash. - static let primary = Color(red: 0.949, green: 0.329, blue: 0.278) + static let primary = Color("BrandingPrimary") /// Secondary gradient color (darker red). - static let secondary = Color(red: 0.65, green: 0.18, blue: 0.15) + static let secondary = Color("BrandingSecondary") /// Accent color for icons and highlights. - static let accent = Color.white + static let accent = Color("BrandingAccent") } } @@ -54,7 +54,7 @@ extension LaunchScreenConfig { primaryColor: Color.Branding.primary, secondaryColor: Color.Branding.secondary, accentColor: Color.Branding.accent, - titleColor: .white, + titleColor: Color.Branding.accent, iconSize: 52, titleSize: 32, iconSpacing: 12, diff --git a/BusinessCard/Design/BusinessCardAccentColors.swift b/BusinessCard/Design/BusinessCardAccentColors.swift index bbf29f4..516ce67 100644 --- a/BusinessCard/Design/BusinessCardAccentColors.swift +++ b/BusinessCard/Design/BusinessCardAccentColors.swift @@ -2,8 +2,8 @@ import SwiftUI import Bedrock public enum BusinessCardAccentColors: AccentColorProvider { - public static let primary = Color(red: 0.95, green: 0.33, blue: 0.28) - public static let light = Color(red: 0.98, green: 0.50, blue: 0.45) - public static let dark = Color(red: 0.75, green: 0.25, blue: 0.22) - public static let secondary = Color(red: 0.12, green: 0.12, blue: 0.14) + public static let primary = Color.Accent.red + public static let light = Color("CardPaletteRose") + public static let dark = Color("BrandingSecondary") + public static let secondary = Color.Accent.ink } diff --git a/BusinessCard/Design/BusinessCardButtonColors.swift b/BusinessCard/Design/BusinessCardButtonColors.swift index c7a06b6..03da87d 100644 --- a/BusinessCard/Design/BusinessCardButtonColors.swift +++ b/BusinessCard/Design/BusinessCardButtonColors.swift @@ -2,9 +2,9 @@ import SwiftUI import Bedrock public enum BusinessCardButtonColors: ButtonColorProvider { - public static let primaryLight = Color(red: 0.98, green: 0.45, blue: 0.40) - public static let primaryDark = Color(red: 0.85, green: 0.28, blue: 0.24) - public static let secondary = Color(red: 0.14, green: 0.14, blue: 0.17).opacity(Design.Opacity.subtle) - public static let destructive = Color.red.opacity(Design.Opacity.heavy) - public static let cancelText = Color(red: 0.32, green: 0.34, blue: 0.40) + public static let primaryLight = Color("CardPaletteRose") + public static let primaryDark = Color("BrandingSecondary") + public static let secondary = Color.AppBackground.accent.opacity(Design.Opacity.subtle) + public static let destructive = Color.Accent.red.opacity(Design.Opacity.heavy) + public static let cancelText = Color.Text.secondary } diff --git a/BusinessCard/Design/BusinessCardInteractiveColors.swift b/BusinessCard/Design/BusinessCardInteractiveColors.swift index 2fb03cd..7909a59 100644 --- a/BusinessCard/Design/BusinessCardInteractiveColors.swift +++ b/BusinessCard/Design/BusinessCardInteractiveColors.swift @@ -3,7 +3,7 @@ import Bedrock public enum BusinessCardInteractiveColors: InteractiveColorProvider { public static let selected = BusinessCardAccentColors.primary.opacity(Design.Opacity.selection) - public static let hover = Color(red: 0.14, green: 0.14, blue: 0.17).opacity(Design.Opacity.subtle) - public static let pressed = Color(red: 0.14, green: 0.14, blue: 0.17).opacity(Design.Opacity.hint) + public static let hover = Color.AppBackground.accent.opacity(Design.Opacity.subtle) + public static let pressed = Color.AppBackground.accent.opacity(Design.Opacity.hint) public static let focus = BusinessCardAccentColors.light } diff --git a/BusinessCard/Design/BusinessCardStatusColors.swift b/BusinessCard/Design/BusinessCardStatusColors.swift index 0b6ac69..0c809fc 100644 --- a/BusinessCard/Design/BusinessCardStatusColors.swift +++ b/BusinessCard/Design/BusinessCardStatusColors.swift @@ -2,8 +2,8 @@ import SwiftUI import Bedrock public enum BusinessCardStatusColors: StatusColorProvider { - public static let success = Color(red: 0.2, green: 0.75, blue: 0.4) - public static let warning = Color(red: 0.95, green: 0.75, blue: 0.25) - public static let error = Color(red: 0.9, green: 0.3, blue: 0.3) - public static let info = Color(red: 0.3, green: 0.6, blue: 0.9) + public static let success = Color.Accent.mint + public static let warning = Color.Accent.gold + public static let error = Color.Accent.red + public static let info = Color("SocialTwitter") } diff --git a/BusinessCard/Design/DesignConstants.swift b/BusinessCard/Design/DesignConstants.swift index d23e878..786e23c 100644 --- a/BusinessCard/Design/DesignConstants.swift +++ b/BusinessCard/Design/DesignConstants.swift @@ -91,6 +91,42 @@ extension Design { DeviceSizeTier.isTablet ? 500 : nil } } + + /// Contacts feature constants (list behavior, row layout, note UI). + enum Contacts { + static let detailFieldIconWidth: CGFloat = 28 + static let detailHeaderAvatarSize: CGFloat = 92 + static let sectionIndexMinContacts = 20 + static let sectionIndexMinSections = 6 + static let sectionIndexLetterSpacing: CGFloat = 2 + static let sectionIndexLetterWidth: CGFloat = 16 + static let sectionIndexLetterHeight: CGFloat = 12 + static let sectionIndexScrollAnimationDuration: Double = 0.2 + static let notePreviewLineLimit = 2 + } + + /// Image color extraction constants. + enum ColorExtraction { + static let sampleSize = 50 + static let rgbaBytesPerPixel = 4 + static let alphaVisibilityThreshold: UInt8 = 128 + static let minBrightness = 30 + static let maxBrightness = 225 + static let bucketQuantizationStep = 32 + static let rgbDenominator = 255.0 + static let colorSimilarityThreshold = 0.15 + } + + /// Card theme math constants. + enum ThemeMath { + static let luminanceRedWeight = 0.299 + static let luminanceGreenWeight = 0.587 + static let luminanceBlueWeight = 0.114 + static let requiresDarkTextThreshold = 0.5 + static let darkThemeLuminanceThreshold = 0.3 + static let customLightenAmount = 0.15 + static let customDarkenAmount = 0.12 + } } // MARK: - Shadow Extensions @@ -118,27 +154,27 @@ extension Color { // MARK: - Card Theme Palette enum CardPalette { - static let coral = Color(red: 0.95, green: 0.35, blue: 0.33) - static let midnight = Color(red: 0.12, green: 0.16, blue: 0.22) - static let ocean = Color(red: 0.08, green: 0.45, blue: 0.56) - static let lime = Color(red: 0.73, green: 0.82, blue: 0.34) - static let violet = Color(red: 0.42, green: 0.36, blue: 0.62) - static let forest = Color(red: 0.13, green: 0.37, blue: 0.31) - static let rose = Color(red: 0.95, green: 0.68, blue: 0.73) - static let slate = Color(red: 0.38, green: 0.44, blue: 0.50) - static let amber = Color(red: 0.98, green: 0.75, blue: 0.28) - static let plum = Color(red: 0.56, green: 0.27, blue: 0.52) - static let sand = Color(red: 0.93, green: 0.83, blue: 0.68) + static let coral = Color("CardPaletteCoral") + static let midnight = Color("CardPaletteMidnight") + static let ocean = Color("CardPaletteOcean") + static let lime = Color("CardPaletteLime") + static let violet = Color("CardPaletteViolet") + static let forest = Color("CardPaletteForest") + static let rose = Color("CardPaletteRose") + static let slate = Color("CardPaletteSlate") + static let amber = Color("CardPaletteAmber") + static let plum = Color("CardPalettePlum") + static let sand = Color("CardPaletteSand") } // MARK: - App Accent Colors enum AppAccent { - static let red = Color(red: 0.95, green: 0.33, blue: 0.28) - static let gold = Color(red: 0.95, green: 0.75, blue: 0.25) - static let mint = Color(red: 0.2, green: 0.65, blue: 0.55) - static let ink = Color(red: 0.12, green: 0.12, blue: 0.14) - static let slate = Color(red: 0.29, green: 0.33, blue: 0.4) + static let red = Color("AppAccentRed") + static let gold = Color("AppAccentGold") + static let mint = Color("AppAccentMint") + static let ink = Color("AppAccentInk") + static let slate = Color("AppAccentSlate") } // MARK: - App Text Colors @@ -153,8 +189,8 @@ extension Color { // MARK: - Badge Colors enum Badge { - static let star = Color(red: 0.98, green: 0.82, blue: 0.34) - static let neutral = Color(red: 0.89, green: 0.89, blue: 0.9) + static let star = Color("BadgeStar") + static let neutral = Color("BadgeNeutral") } // MARK: - Share Sheet Theme @@ -170,17 +206,17 @@ extension Color { // MARK: - Social Media Brand Colors enum Social { - static let linkedIn = Color(red: 0.0, green: 0.47, blue: 0.71) - static let twitter = Color(red: 0.11, green: 0.63, blue: 0.95) - static let instagram = Color(red: 0.88, green: 0.19, blue: 0.42) - static let facebook = Color(red: 0.26, green: 0.40, blue: 0.70) - static let tiktok = Color(red: 0.0, green: 0.0, blue: 0.0) - static let github = Color(red: 0.13, green: 0.13, blue: 0.13) - static let threads = Color(red: 0.0, green: 0.0, blue: 0.0) - static let telegram = Color(red: 0.16, green: 0.63, blue: 0.89) - static let whatsapp = Color(red: 0.15, green: 0.68, blue: 0.38) - static let venmo = Color(red: 0.22, green: 0.53, blue: 0.79) - static let cashApp = Color(red: 0.0, green: 0.82, blue: 0.35) + static let linkedIn = Color("SocialLinkedIn") + static let twitter = Color("SocialTwitter") + static let instagram = Color("SocialInstagram") + static let facebook = Color("SocialFacebook") + static let tiktok = Color("SocialTikTok") + static let github = Color("SocialGitHub") + static let threads = Color("SocialThreads") + static let telegram = Color("SocialTelegram") + static let whatsapp = Color("SocialWhatsApp") + static let venmo = Color("SocialVenmo") + static let cashApp = Color("SocialCashApp") } } diff --git a/BusinessCard/Models/CardTheme.swift b/BusinessCard/Models/CardTheme.swift index 65c775d..7d3f69c 100644 --- a/BusinessCard/Models/CardTheme.swift +++ b/BusinessCard/Models/CardTheme.swift @@ -1,4 +1,5 @@ import SwiftUI +import Bedrock /// Card theme configuration supporting both preset themes and custom colors. /// @@ -88,8 +89,11 @@ struct CardTheme: Identifiable, Hashable, Sendable { var requiresDarkText: Bool { if let rgb = customRGB { // Calculate perceived luminance for custom colors - let luminance = 0.299 * rgb.0 + 0.587 * rgb.1 + 0.114 * rgb.2 - return luminance > 0.5 + let luminance = + Design.ThemeMath.luminanceRedWeight * rgb.0 + + Design.ThemeMath.luminanceGreenWeight * rgb.1 + + Design.ThemeMath.luminanceBlueWeight * rgb.2 + return luminance > Design.ThemeMath.requiresDarkTextThreshold } guard let preset else { return false } switch preset { @@ -100,106 +104,82 @@ struct CardTheme: Identifiable, Hashable, Sendable { } } - // MARK: - RGB Values - - private var primaryRGB: (Double, Double, Double) { - if let rgb = customRGB { return rgb } - guard let preset else { return (0.95, 0.35, 0.33) } - switch preset { - case .coral: return (0.95, 0.35, 0.33) - case .midnight: return (0.12, 0.16, 0.22) - case .ocean: return (0.08, 0.45, 0.56) - case .lime: return (0.73, 0.82, 0.34) - case .violet: return (0.42, 0.36, 0.62) - case .forest: return (0.13, 0.37, 0.31) - case .rose: return (0.95, 0.68, 0.73) - case .slate: return (0.38, 0.44, 0.50) - case .amber: return (0.98, 0.75, 0.28) - case .plum: return (0.56, 0.27, 0.52) - } - } - - private var secondaryRGB: (Double, Double, Double) { - if let rgb = customRGB { - // Calculate luminance to determine if we should lighten or darken - let luminance = 0.299 * rgb.0 + 0.587 * rgb.1 + 0.114 * rgb.2 - - if luminance < 0.3 { - // Dark color: lighten uniformly to avoid color shifts - let lightenAmount = 0.15 - return ( - min(1.0, rgb.0 + lightenAmount), - min(1.0, rgb.1 + lightenAmount), - min(1.0, rgb.2 + lightenAmount) - ) - } else { - // Light color: darken uniformly - let darkenAmount = 0.12 - return ( - max(0.0, rgb.0 - darkenAmount), - max(0.0, rgb.1 - darkenAmount), - max(0.0, rgb.2 - darkenAmount) - ) - } - } - guard let preset else { return (0.93, 0.83, 0.68) } - switch preset { - case .coral: return (0.93, 0.83, 0.68) - case .midnight: return (0.29, 0.33, 0.4) - case .ocean: return (0.2, 0.65, 0.55) - case .lime: return (0.93, 0.83, 0.68) - case .violet: return (0.29, 0.33, 0.4) - case .forest: return (0.22, 0.52, 0.42) - case .rose: return (0.98, 0.85, 0.88) - case .slate: return (0.52, 0.58, 0.64) - case .amber: return (0.99, 0.88, 0.55) - case .plum: return (0.72, 0.45, 0.68) - } - } - - private var accentRGB: (Double, Double, Double) { - if customRGB != nil { - // For custom colors, use a contrasting accent (gold or dark) - return requiresDarkText ? (0.12, 0.12, 0.14) : (0.95, 0.75, 0.25) - } - guard let preset else { return (0.95, 0.33, 0.28) } - switch preset { - case .coral: return (0.95, 0.33, 0.28) - case .midnight: return (0.95, 0.75, 0.25) - case .ocean: return (0.95, 0.75, 0.25) - case .lime: return (0.12, 0.12, 0.14) - case .violet: return (0.95, 0.75, 0.25) - case .forest: return (0.95, 0.75, 0.25) - case .rose: return (0.75, 0.25, 0.35) - case .slate: return (0.95, 0.75, 0.25) - case .amber: return (0.12, 0.12, 0.14) - case .plum: return (0.95, 0.75, 0.25) - } - } - - private var textRGB: (Double, Double, Double) { - requiresDarkText - ? (0.14, 0.14, 0.17) // Dark text for light backgrounds - : (0.98, 0.98, 0.98) // Light text for dark backgrounds - } - // MARK: - Colors (MainActor) @MainActor var primaryColor: Color { - Color(red: primaryRGB.0, green: primaryRGB.1, blue: primaryRGB.2) + if let rgb = customRGB { + return Color(red: rgb.0, green: rgb.1, blue: rgb.2) + } + + guard let preset else { return Color.CardPalette.coral } + switch preset { + case .coral: return Color.CardPalette.coral + case .midnight: return Color.CardPalette.midnight + case .ocean: return Color.CardPalette.ocean + case .lime: return Color.CardPalette.lime + case .violet: return Color.CardPalette.violet + case .forest: return Color.CardPalette.forest + case .rose: return Color.CardPalette.rose + case .slate: return Color.CardPalette.slate + case .amber: return Color.CardPalette.amber + case .plum: return Color.CardPalette.plum + } } @MainActor var secondaryColor: Color { - Color(red: secondaryRGB.0, green: secondaryRGB.1, blue: secondaryRGB.2) + if let rgb = customRGB { + // Calculate luminance to determine if we should lighten or darken. + let luminance = + Design.ThemeMath.luminanceRedWeight * rgb.0 + + Design.ThemeMath.luminanceGreenWeight * rgb.1 + + Design.ThemeMath.luminanceBlueWeight * rgb.2 + if luminance < Design.ThemeMath.darkThemeLuminanceThreshold { + return Color( + red: min(1.0, rgb.0 + Design.ThemeMath.customLightenAmount), + green: min(1.0, rgb.1 + Design.ThemeMath.customLightenAmount), + blue: min(1.0, rgb.2 + Design.ThemeMath.customLightenAmount) + ) + } else { + return Color( + red: max(0.0, rgb.0 - Design.ThemeMath.customDarkenAmount), + green: max(0.0, rgb.1 - Design.ThemeMath.customDarkenAmount), + blue: max(0.0, rgb.2 - Design.ThemeMath.customDarkenAmount) + ) + } + } + + guard let preset else { return Color.CardPalette.sand } + switch preset { + case .coral, .lime: return Color.CardPalette.sand + case .midnight, .violet: return Color.Accent.slate + case .ocean: return Color.Accent.mint + case .forest: return Color("SocialWhatsApp") + case .rose: return Color("CardPaletteRose") + case .slate: return Color("CardPaletteSlate") + case .amber: return Color("CardPaletteSand") + case .plum: return Color("CardPalettePlum") + } } @MainActor var accentColor: Color { - Color(red: accentRGB.0, green: accentRGB.1, blue: accentRGB.2) + if customRGB != nil { + return requiresDarkText ? Color.Accent.ink : Color.Accent.gold + } + + guard let preset else { return Color.Accent.red } + switch preset { + case .lime, .amber: + return Color.Accent.ink + case .rose: + return Color("BrandingSecondary") + case .coral, .midnight, .ocean, .violet, .forest, .slate, .plum: + return Color.Accent.gold + } } /// The appropriate text color for content displayed on this theme's background. @MainActor var textColor: Color { - Color(red: textRGB.0, green: textRGB.1, blue: textRGB.2) + requiresDarkText ? Color.Text.primary : Color.AppText.inverted } // MARK: - Hashable & Equatable diff --git a/BusinessCard/Models/ContactFieldType.swift b/BusinessCard/Models/ContactFieldType.swift index d3bd377..38f84de 100644 --- a/BusinessCard/Models/ContactFieldType.swift +++ b/BusinessCard/Models/ContactFieldType.swift @@ -117,7 +117,7 @@ extension ContactFieldType { id: "phone", displayName: String(localized: "Phone Number"), systemImage: "phone.fill", - iconColor: Color(red: 0.2, green: 0.2, blue: 0.2), + iconColor: Color.Text.secondary, category: .contact, valueLabel: String(localized: "Phone Number"), valuePlaceholder: "+1 (555) 123-4567", @@ -134,7 +134,7 @@ extension ContactFieldType { id: "email", displayName: String(localized: "Email"), systemImage: "envelope.fill", - iconColor: Color(red: 0.2, green: 0.2, blue: 0.2), + iconColor: Color.Text.secondary, category: .contact, valueLabel: String(localized: "Email"), valuePlaceholder: "you@example.com", @@ -147,7 +147,7 @@ extension ContactFieldType { id: "website", displayName: String(localized: "Company Website"), systemImage: "globe", - iconColor: Color(red: 0.2, green: 0.2, blue: 0.2), + iconColor: Color.Text.secondary, category: .contact, valueLabel: String(localized: "Website URL"), valuePlaceholder: "https://company.com", @@ -160,7 +160,7 @@ extension ContactFieldType { id: "address", displayName: String(localized: "Address"), systemImage: "location.fill", - iconColor: Color(red: 0.2, green: 0.2, blue: 0.2), + iconColor: Color.Text.secondary, category: .contact, valueLabel: String(localized: "Address"), valuePlaceholder: "123 Main St, City, State", @@ -187,7 +187,7 @@ extension ContactFieldType { displayName: "LinkedIn", systemImage: "linkedin", isCustomSymbol: true, - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .social, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "linkedin.com/in/username", @@ -201,7 +201,7 @@ extension ContactFieldType { displayName: "X", systemImage: "x-twitter", isCustomSymbol: true, - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .social, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "x.com/username", @@ -215,7 +215,7 @@ extension ContactFieldType { displayName: "Instagram", systemImage: "instagram", isCustomSymbol: true, - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .social, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "instagram.com/username", @@ -229,7 +229,7 @@ extension ContactFieldType { displayName: "Facebook", systemImage: "facebook", isCustomSymbol: true, - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .social, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "facebook.com/username", @@ -243,7 +243,7 @@ extension ContactFieldType { displayName: "TikTok", systemImage: "tiktok", isCustomSymbol: true, - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .social, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "tiktok.com/@username", @@ -257,7 +257,7 @@ extension ContactFieldType { displayName: "Threads", systemImage: "threads", isCustomSymbol: true, - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .social, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "threads.net/@username", @@ -271,7 +271,7 @@ extension ContactFieldType { displayName: "YouTube", systemImage: "youtube", isCustomSymbol: true, - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .social, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "youtube.com/@channel", @@ -284,7 +284,7 @@ extension ContactFieldType { id: "snapchat", displayName: "Snapchat", systemImage: "camera.fill", - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .social, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "snapchat.com/add/username", @@ -297,7 +297,7 @@ extension ContactFieldType { id: "pinterest", displayName: "Pinterest", systemImage: "pin.fill", - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .social, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "pinterest.com/username", @@ -311,7 +311,7 @@ extension ContactFieldType { displayName: "Twitch", systemImage: "twitch", isCustomSymbol: true, - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .social, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "twitch.tv/username", @@ -325,7 +325,7 @@ extension ContactFieldType { displayName: "Bluesky", systemImage: "bluesky", isCustomSymbol: true, - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .social, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "bsky.app/profile/username", @@ -339,7 +339,7 @@ extension ContactFieldType { displayName: "Mastodon", systemImage: "mastodon", isCustomSymbol: true, - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .social, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "mastodon.social/@username", @@ -362,7 +362,7 @@ extension ContactFieldType { displayName: "Reddit", systemImage: "reddit", isCustomSymbol: true, - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .social, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "reddit.com/user/username", @@ -378,7 +378,7 @@ extension ContactFieldType { displayName: "GitHub", systemImage: "github.fill", isCustomSymbol: true, - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .developer, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "github.com/username", @@ -391,7 +391,7 @@ extension ContactFieldType { id: "gitlab", displayName: "GitLab", systemImage: "chevron.left.forwardslash.chevron.right", - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .developer, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "gitlab.com/username", @@ -404,7 +404,7 @@ extension ContactFieldType { id: "stackoverflow", displayName: "Stack Overflow", systemImage: "text.bubble.fill", - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .developer, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "stackoverflow.com/users/id", @@ -420,7 +420,7 @@ extension ContactFieldType { displayName: "Telegram", systemImage: "telegram", isCustomSymbol: true, - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .messaging, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "t.me/username", @@ -433,7 +433,7 @@ extension ContactFieldType { id: "whatsapp", displayName: "WhatsApp", systemImage: "message.fill", - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .messaging, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "+1 555 123 4567", @@ -449,7 +449,7 @@ extension ContactFieldType { id: "signal", displayName: "Signal", systemImage: "bubble.left.fill", - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .messaging, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "+1 555 123 4567", @@ -466,7 +466,7 @@ extension ContactFieldType { displayName: "Discord", systemImage: "discord", isCustomSymbol: true, - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .messaging, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "discord.gg/invite", @@ -480,7 +480,7 @@ extension ContactFieldType { displayName: "Slack", systemImage: "slack", isCustomSymbol: true, - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .messaging, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "yourworkspace.slack.com", @@ -494,7 +494,7 @@ extension ContactFieldType { displayName: "Matrix", systemImage: "matrix", isCustomSymbol: true, - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .messaging, valueLabel: String(localized: "Username/Link"), valuePlaceholder: "@username:matrix.org", @@ -515,7 +515,7 @@ extension ContactFieldType { id: "venmo", displayName: "Venmo", systemImage: "dollarsign.circle.fill", - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .payment, valueLabel: String(localized: "Username"), valuePlaceholder: "@username", @@ -528,7 +528,7 @@ extension ContactFieldType { id: "cashApp", displayName: "Cash App", systemImage: "dollarsign.square.fill", - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .payment, valueLabel: String(localized: "Username"), valuePlaceholder: "$cashtag", @@ -541,7 +541,7 @@ extension ContactFieldType { id: "paypal", displayName: "PayPal", systemImage: "creditcard.fill", - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .payment, valueLabel: String(localized: "Email or Username"), valuePlaceholder: "paypal.me/username", @@ -554,7 +554,7 @@ extension ContactFieldType { id: "zelle", displayName: "Zelle", systemImage: "dollarsign.arrow.circlepath", - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .payment, valueLabel: String(localized: "Phone or Email"), valuePlaceholder: "email@example.com", @@ -570,7 +570,7 @@ extension ContactFieldType { displayName: "Patreon", systemImage: "patreon.fill", isCustomSymbol: true, - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .creator, valueLabel: String(localized: "Profile Link"), valuePlaceholder: "patreon.com/username", @@ -584,7 +584,7 @@ extension ContactFieldType { displayName: "Ko-fi", systemImage: "ko-fi", isCustomSymbol: true, - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .creator, valueLabel: String(localized: "Profile Link"), valuePlaceholder: "ko-fi.com/username", @@ -599,7 +599,7 @@ extension ContactFieldType { id: "calendly", displayName: "Calendly", systemImage: "calendar", - iconColor: Color(red: 0.0, green: 0.0, blue: 0.0), + iconColor: Color.Text.primary, category: .scheduling, valueLabel: String(localized: "Calendly Link"), valuePlaceholder: "calendly.com/username", @@ -614,7 +614,7 @@ extension ContactFieldType { id: "customLink", displayName: String(localized: "Link"), systemImage: "link", - iconColor: Color(red: 0.2, green: 0.2, blue: 0.2), + iconColor: Color.Text.secondary, category: .other, valueLabel: String(localized: "URL"), valuePlaceholder: "https://example.com", diff --git a/BusinessCard/Services/ColorExtractor.swift b/BusinessCard/Services/ColorExtractor.swift index 2861aff..ffd3275 100644 --- a/BusinessCard/Services/ColorExtractor.swift +++ b/BusinessCard/Services/ColorExtractor.swift @@ -1,10 +1,11 @@ import SwiftUI import UIKit +import Bedrock extension UIImage { /// Extracts dominant colors from the image using pixel sampling. /// - Parameter count: Number of colors to extract (default 3) - /// - Returns: Array of SwiftUI Colors, always includes white and black as fallbacks + /// - Returns: Array of SwiftUI Colors, always includes app theme fallback colors func dominantColors(count: Int = 3) -> [Color] { guard let cgImage = self.cgImage else { return defaultColors(count: count) @@ -18,14 +19,14 @@ extension UIImage { } // Create a smaller sample size for performance - let sampleSize = 50 + let sampleSize = Design.ColorExtraction.sampleSize guard let context = CGContext( data: nil, width: sampleSize, height: sampleSize, bitsPerComponent: 8, - bytesPerRow: sampleSize * 4, + bytesPerRow: sampleSize * Design.ColorExtraction.rgbaBytesPerPixel, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue ) else { @@ -38,7 +39,10 @@ extension UIImage { return defaultColors(count: count) } - let data = pixelData.bindMemory(to: UInt8.self, capacity: sampleSize * sampleSize * 4) + let data = pixelData.bindMemory( + to: UInt8.self, + capacity: sampleSize * sampleSize * Design.ColorExtraction.rgbaBytesPerPixel + ) // Collect colors with their frequencies var colorCounts: [ColorBucket: Int] = [:] @@ -52,17 +56,18 @@ extension UIImage { let a = data[offset + 3] // Skip transparent or near-transparent pixels - guard a > 128 else { continue } + guard a > Design.ColorExtraction.alphaVisibilityThreshold else { continue } // Skip near-white and near-black (we'll add those as defaults) let brightness = (Int(r) + Int(g) + Int(b)) / 3 - guard brightness > 30 && brightness < 225 else { continue } + guard brightness > Design.ColorExtraction.minBrightness + && brightness < Design.ColorExtraction.maxBrightness else { continue } // Bucket colors to reduce noise (group similar colors) let bucket = ColorBucket( - r: UInt8((Int(r) / 32) * 32), - g: UInt8((Int(g) / 32) * 32), - b: UInt8((Int(b) / 32) * 32) + r: UInt8((Int(r) / Design.ColorExtraction.bucketQuantizationStep) * Design.ColorExtraction.bucketQuantizationStep), + g: UInt8((Int(g) / Design.ColorExtraction.bucketQuantizationStep) * Design.ColorExtraction.bucketQuantizationStep), + b: UInt8((Int(b) / Design.ColorExtraction.bucketQuantizationStep) * Design.ColorExtraction.bucketQuantizationStep) ) colorCounts[bucket, default: 0] += 1 @@ -77,9 +82,9 @@ extension UIImage { // Add top colors, ensuring they're distinct for (bucket, _) in sortedColors { let color = Color( - red: Double(bucket.r) / 255.0, - green: Double(bucket.g) / 255.0, - blue: Double(bucket.b) / 255.0 + red: Double(bucket.r) / Design.ColorExtraction.rgbDenominator, + green: Double(bucket.g) / Design.ColorExtraction.rgbDenominator, + blue: Double(bucket.b) / Design.ColorExtraction.rgbDenominator ) // Check if this color is distinct enough from existing ones @@ -92,20 +97,20 @@ extension UIImage { } } - // Always include white as first option - var result: [Color] = [.white] + // Always include high-contrast app fallback colors first. + var result: [Color] = [Color.AppText.inverted] result.append(contentsOf: extractedColors) - // Add black if we have room + // Add primary text color if we have room. if result.count < count { - result.append(.black) + result.append(Color.Text.primary) } return Array(result.prefix(count)) } private func defaultColors(count: Int) -> [Color] { - let defaults: [Color] = [.white, .black, Color(red: 0.2, green: 0.2, blue: 0.2)] + let defaults: [Color] = [Color.AppText.inverted, Color.Text.primary, Color.Text.secondary] return Array(defaults.prefix(count)) } } @@ -122,7 +127,7 @@ private struct ColorBucket: Hashable { private extension Color { /// Checks if two colors are visually similar. - func isClose(to other: Color, threshold: Double = 0.15) -> Bool { + func isClose(to other: Color, threshold: Double = Design.ColorExtraction.colorSimilarityThreshold) -> Bool { guard let selfComponents = self.cgColor?.components, let otherComponents = other.cgColor?.components, selfComponents.count >= 3, @@ -137,4 +142,3 @@ private extension Color { return rDiff < threshold && gDiff < threshold && bDiff < threshold } } - diff --git a/BusinessCard/Views/Features/AppShell/FloatingShareButton.swift b/BusinessCard/Views/Features/AppShell/FloatingShareButton.swift index fc6b5ed..093b8eb 100644 --- a/BusinessCard/Views/Features/AppShell/FloatingShareButton.swift +++ b/BusinessCard/Views/Features/AppShell/FloatingShareButton.swift @@ -9,7 +9,7 @@ struct FloatingShareButton: View { Image(systemName: "qrcode") .typography(.title2) .fontWeight(.semibold) - .foregroundStyle(.white) + .foregroundStyle(Color.AppText.inverted) .frame(width: Design.CardSize.floatingButtonSize, height: Design.CardSize.floatingButtonSize) .background( Circle() diff --git a/BusinessCard/Views/Features/Cards/CardsHomeView.swift b/BusinessCard/Views/Features/Cards/CardsHomeView.swift index 3782140..d996a76 100644 --- a/BusinessCard/Views/Features/Cards/CardsHomeView.swift +++ b/BusinessCard/Views/Features/Cards/CardsHomeView.swift @@ -32,6 +32,8 @@ struct CardsHomeView: View { } .navigationTitle(String.localized("My Cards")) .navigationBarTitleDisplayMode(.inline) + .toolbarBackground(.ultraThinMaterial, for: .navigationBar) + .toolbarBackground(.visible, for: .navigationBar) .toolbar { ToolbarItem(placement: .topBarLeading) { Button(String.localized("Add Card"), systemImage: "plus") { diff --git a/BusinessCard/Views/Features/Cards/Components/BusinessCardView.swift b/BusinessCard/Views/Features/Cards/Components/BusinessCardView.swift index 3b498c6..aaddd2f 100644 --- a/BusinessCard/Views/Features/Cards/Components/BusinessCardView.swift +++ b/BusinessCard/Views/Features/Cards/Components/BusinessCardView.swift @@ -573,7 +573,7 @@ private struct ContactFieldRowView: View { .background(Color.AppBackground.base.opacity(0.82)) .overlay( RoundedRectangle(cornerRadius: Design.CornerRadius.medium) - .stroke(Color.white.opacity(0.08), lineWidth: 1) + .stroke(Color.Text.tertiary.opacity(Design.Opacity.light), lineWidth: Design.LineWidth.thin) ) .clipShape(.rect(cornerRadius: Design.CornerRadius.medium)) .contentShape(.rect) diff --git a/BusinessCard/Views/Features/Cards/Editor/CardEditorView.swift b/BusinessCard/Views/Features/Cards/Editor/CardEditorView.swift index 8840c2c..5ea2f60 100644 --- a/BusinessCard/Views/Features/Cards/Editor/CardEditorView.swift +++ b/BusinessCard/Views/Features/Cards/Editor/CardEditorView.swift @@ -410,8 +410,8 @@ private struct CustomColorSwatch: View { if customColor == nil { Image(systemName: "eyedropper") .typography(.caption) - .foregroundStyle(.white) - .shadow(color: .black.opacity(Design.Opacity.medium), radius: Design.Shadow.radiusSmall) + .foregroundStyle(Color.AppText.inverted) + .shadow(color: Color.AppBackground.base.opacity(Design.Opacity.medium), radius: Design.Shadow.radiusSmall) } } .frame(width: Design.CardSize.colorSwatchSize, height: Design.CardSize.colorSwatchSize) @@ -945,7 +945,7 @@ private struct ContactFieldRowView: View { .overlay( field.fieldType.iconImage() .typography(.title3) - .foregroundStyle(.white) + .foregroundStyle(Color.AppText.inverted) ) // Content @@ -1064,8 +1064,8 @@ private struct PreviewCardButton: View { RoundedRectangle(cornerRadius: Design.CornerRadius.large) .stroke( colorScheme == .dark - ? Color.white.opacity(Design.Opacity.subtle) - : Color.black.opacity(Design.Opacity.light), + ? Color.AppText.inverted.opacity(Design.Opacity.subtle) + : Color.Text.primary.opacity(Design.Opacity.light), lineWidth: Design.LineWidth.thin ) ) diff --git a/BusinessCard/Views/Features/Cards/Sheets/ContactFieldEditorSheet.swift b/BusinessCard/Views/Features/Cards/Sheets/ContactFieldEditorSheet.swift index d1887d4..55bfe96 100644 --- a/BusinessCard/Views/Features/Cards/Sheets/ContactFieldEditorSheet.swift +++ b/BusinessCard/Views/Features/Cards/Sheets/ContactFieldEditorSheet.swift @@ -26,7 +26,7 @@ struct ContactFieldEditorSheet: View { fieldType: ContactFieldType, initialValue: String = "", initialTitle: String = "", - themeColor: Color = Color(red: 0.2, green: 0.2, blue: 0.2), + themeColor: Color = Color.Text.secondary, onSave: @escaping (String, String) -> Void, onDelete: (() -> Void)? = nil ) { diff --git a/BusinessCard/Views/Features/Cards/Sheets/CropGridLines.swift b/BusinessCard/Views/Features/Cards/Sheets/CropGridLines.swift index 4e7f648..f4b89ae 100644 --- a/BusinessCard/Views/Features/Cards/Sheets/CropGridLines.swift +++ b/BusinessCard/Views/Features/Cards/Sheets/CropGridLines.swift @@ -9,7 +9,7 @@ struct CropGridLines: View { HStack(spacing: cropSize.width / 3 - Design.LineWidth.thin) { ForEach(0..<2, id: \.self) { _ in Rectangle() - .fill(Color.white.opacity(Design.Opacity.light)) + .fill(Color.AppText.inverted.opacity(Design.Opacity.light)) .frame(width: Design.LineWidth.thin, height: cropSize.height) } } @@ -17,7 +17,7 @@ struct CropGridLines: View { VStack(spacing: cropSize.height / 3 - Design.LineWidth.thin) { ForEach(0..<2, id: \.self) { _ in Rectangle() - .fill(Color.white.opacity(Design.Opacity.light)) + .fill(Color.AppText.inverted.opacity(Design.Opacity.light)) .frame(width: cropSize.width, height: Design.LineWidth.thin) } } diff --git a/BusinessCard/Views/Features/Cards/Sheets/CropOverlay.swift b/BusinessCard/Views/Features/Cards/Sheets/CropOverlay.swift index 0d55fad..d1a4d14 100644 --- a/BusinessCard/Views/Features/Cards/Sheets/CropOverlay.swift +++ b/BusinessCard/Views/Features/Cards/Sheets/CropOverlay.swift @@ -8,7 +8,7 @@ struct CropOverlay: View { var body: some View { ZStack { Rectangle() - .fill(Color.black.opacity(Design.Opacity.accent)) + .fill(Color.AppBackground.base.opacity(Design.Opacity.accent)) Rectangle() .fill(Color.clear) @@ -19,7 +19,7 @@ struct CropOverlay: View { .allowsHitTesting(false) Rectangle() - .stroke(Color.white, lineWidth: Design.LineWidth.thin) + .stroke(Color.AppText.inverted, lineWidth: Design.LineWidth.thin) .frame(width: cropSize.width, height: cropSize.height) .allowsHitTesting(false) } diff --git a/BusinessCard/Views/Features/Cards/Sheets/FieldHeaderView.swift b/BusinessCard/Views/Features/Cards/Sheets/FieldHeaderView.swift index 28ff78d..9e9f341 100644 --- a/BusinessCard/Views/Features/Cards/Sheets/FieldHeaderView.swift +++ b/BusinessCard/Views/Features/Cards/Sheets/FieldHeaderView.swift @@ -13,7 +13,7 @@ struct FieldHeaderView: View { .overlay( fieldType.iconImage() .typography(.title3) - .foregroundStyle(.white) + .foregroundStyle(Color.AppText.inverted) ) Text(fieldType.displayName) diff --git a/BusinessCard/Views/Features/Cards/Sheets/PhotoCropperSheet.swift b/BusinessCard/Views/Features/Cards/Sheets/PhotoCropperSheet.swift index 7a94f70..37c96c2 100644 --- a/BusinessCard/Views/Features/Cards/Sheets/PhotoCropperSheet.swift +++ b/BusinessCard/Views/Features/Cards/Sheets/PhotoCropperSheet.swift @@ -89,7 +89,7 @@ struct PhotoCropperSheet: View { GeometryReader { geometry in ZStack { // Dark background - Color.black.ignoresSafeArea() + Color.AppBackground.base.ignoresSafeArea() // Image with gestures if let uiImage { @@ -129,7 +129,7 @@ struct PhotoCropperSheet: View { dismiss() } } - .foregroundStyle(.white) + .foregroundStyle(Color.AppText.inverted) } ToolbarItem(placement: .principal) { HStack(spacing: Design.Spacing.xLarge) { @@ -138,7 +138,7 @@ struct PhotoCropperSheet: View { rotate90Left() } label: { Image(systemName: "rotate.left") - .foregroundStyle(.white) + .foregroundStyle(Color.AppText.inverted) } // Reset all transforms @@ -146,7 +146,7 @@ struct PhotoCropperSheet: View { resetTransform() } label: { Image(systemName: "arrow.counterclockwise") - .foregroundStyle(.white) + .foregroundStyle(Color.AppText.inverted) } // Aspect ratio picker (only for logo workflow) @@ -155,7 +155,7 @@ struct PhotoCropperSheet: View { showingAspectRatioPicker = true } label: { Image(systemName: "aspectratio") - .foregroundStyle(.white) + .foregroundStyle(Color.AppText.inverted) } } @@ -164,7 +164,7 @@ struct PhotoCropperSheet: View { rotate90Right() } label: { Image(systemName: "rotate.right") - .foregroundStyle(.white) + .foregroundStyle(Color.AppText.inverted) } } } @@ -173,7 +173,7 @@ struct PhotoCropperSheet: View { cropAndSave() } .bold() - .foregroundStyle(.white) + .foregroundStyle(Color.AppText.inverted) } } } diff --git a/BusinessCard/Views/Features/Contacts/Components/ContactRowView.swift b/BusinessCard/Views/Features/Contacts/Components/ContactRowView.swift index eab7351..444501a 100644 --- a/BusinessCard/Views/Features/Contacts/Components/ContactRowView.swift +++ b/BusinessCard/Views/Features/Contacts/Components/ContactRowView.swift @@ -18,6 +18,10 @@ struct ContactRowView: View { return trimmed.isEmpty ? String.localized("Shared") : String.localized(trimmed) } + private var chipBackground: Color { Color.AppBackground.accent } + private var chipTextColor: Color { Color.Text.primary } + private var chipStroke: Color { Color.Text.tertiary.opacity(Design.Opacity.light) } + var body: some View { HStack(spacing: Design.Spacing.medium) { ContactAvatarView(contact: contact) @@ -54,10 +58,14 @@ struct ContactRowView: View { ForEach(contact.tagList.prefix(2), id: \.self) { tag in Text(tag) .typography(.caption2) - .foregroundStyle(Color.Text.secondary) + .foregroundStyle(chipTextColor) .padding(.horizontal, Design.Spacing.xSmall) .padding(.vertical, Design.Spacing.xxSmall) - .background(Color.AppBackground.accent.opacity(0.5)) + .background(chipBackground) + .overlay( + RoundedRectangle(cornerRadius: Design.CornerRadius.small) + .stroke(chipStroke, lineWidth: Design.LineWidth.thin) + ) .clipShape(.rect(cornerRadius: Design.CornerRadius.small)) } } diff --git a/BusinessCard/Views/Features/Contacts/Components/ContactsListView.swift b/BusinessCard/Views/Features/Contacts/Components/ContactsListView.swift index 5b6fe92..5efc444 100644 --- a/BusinessCard/Views/Features/Contacts/Components/ContactsListView.swift +++ b/BusinessCard/Views/Features/Contacts/Components/ContactsListView.swift @@ -33,7 +33,8 @@ struct ContactsListView: View { } private var showsJumpIndex: Bool { - sortedContacts.count >= 20 && sectionTitles.count >= 6 + sortedContacts.count >= Design.Contacts.sectionIndexMinContacts + && sectionTitles.count >= Design.Contacts.sectionIndexMinSections } var body: some View { @@ -67,17 +68,20 @@ struct ContactsListView: View { } if showsJumpIndex { - VStack(spacing: 2) { + VStack(spacing: Design.Contacts.sectionIndexLetterSpacing) { ForEach(sectionTitles, id: \.self) { title in Button { - withAnimation(.easeInOut(duration: 0.2)) { + withAnimation(.easeInOut(duration: Design.Contacts.sectionIndexScrollAnimationDuration)) { proxy.scrollTo(title, anchor: .top) } } label: { Text(title) .typography(.caption2) .foregroundStyle(Color.Text.secondary) - .frame(width: 16, height: 12) + .frame( + width: Design.Contacts.sectionIndexLetterWidth, + height: Design.Contacts.sectionIndexLetterHeight + ) } .buttonStyle(.plain) } diff --git a/BusinessCard/Views/Features/Contacts/Detail/ContactDetailView.swift b/BusinessCard/Views/Features/Contacts/Detail/ContactDetailView.swift index 0e2aabe..34762ac 100644 --- a/BusinessCard/Views/Features/Contacts/Detail/ContactDetailView.swift +++ b/BusinessCard/Views/Features/Contacts/Detail/ContactDetailView.swift @@ -334,7 +334,7 @@ private struct ContactBannerView: View { .overlay(alignment: .bottomTrailing) { Image(systemName: "pencil") .typography(.caption) - .foregroundStyle(Color.white) + .foregroundStyle(Color.AppText.inverted) .padding(Design.Spacing.xSmall) .background(Color.CardPalette.coral) .clipShape(.circle) @@ -383,7 +383,7 @@ private struct ContactProfileAvatarView: View { .background(Color.CardPalette.coral) } } - .frame(width: 92, height: 92) + .frame(width: Design.Contacts.detailHeaderAvatarSize, height: Design.Contacts.detailHeaderAvatarSize) .clipShape(.circle) .overlay(Circle().stroke(Color.AppBackground.elevated, lineWidth: Design.LineWidth.thick)) .shadow( @@ -451,7 +451,7 @@ private struct ContactInfoCard: View { if index < allContactFields.count - 1 || hasLegacyFields { Divider() - .padding(.leading, 28 + Design.Spacing.medium) + .padding(.leading, Design.Contacts.detailFieldIconWidth + Design.Spacing.medium) } } @@ -466,7 +466,7 @@ private struct ContactInfoCard: View { if !contact.email.isEmpty && contact.emailAddresses.isEmpty { Divider() - .padding(.leading, 28 + Design.Spacing.medium) + .padding(.leading, Design.Contacts.detailFieldIconWidth + Design.Spacing.medium) } } @@ -501,14 +501,14 @@ private struct ContactFieldInfoRow: View { field.iconImage() .typography(.subheading) .foregroundStyle(Color.CardPalette.coral) - .frame(width: 28) + .frame(width: Design.Contacts.detailFieldIconWidth) VStack(alignment: .leading, spacing: Design.Spacing.xxxSmall) { Text(field.displayValue) .typography(.subheading) .foregroundStyle(Color.Text.primary) .multilineTextAlignment(.leading) - .lineLimit(2) + .lineLimit(Design.Contacts.notePreviewLineLimit) Text(field.title.isEmpty ? field.displayName : field.title) .typography(.caption) @@ -539,14 +539,14 @@ private struct ContactInfoRow: View { Image(systemName: icon) .typography(.subheading) .foregroundStyle(Color.CardPalette.coral) - .frame(width: 28) + .frame(width: Design.Contacts.detailFieldIconWidth) VStack(alignment: .leading, spacing: Design.Spacing.xxxSmall) { Text(value) .typography(.subheading) .foregroundStyle(Color.Text.primary) .multilineTextAlignment(.leading) - .lineLimit(2) + .lineLimit(Design.Contacts.notePreviewLineLimit) Text(label) .typography(.caption) .foregroundStyle(Color.Text.secondary) diff --git a/BusinessCard/Views/Features/Share/ScannerOverlayView.swift b/BusinessCard/Views/Features/Share/ScannerOverlayView.swift index 9fa1c8d..2c7461f 100644 --- a/BusinessCard/Views/Features/Share/ScannerOverlayView.swift +++ b/BusinessCard/Views/Features/Share/ScannerOverlayView.swift @@ -7,7 +7,7 @@ struct ScannerOverlayView: View { let size = min(geometry.size.width, geometry.size.height) * 0.7 ZStack { - Color.black.opacity(Design.Opacity.medium) + Color.AppBackground.base.opacity(Design.Opacity.medium) RoundedRectangle(cornerRadius: Design.CornerRadius.large) .frame(width: size, height: size) @@ -27,7 +27,7 @@ struct ScannerOverlayView: View { .typography(.heading) .foregroundStyle(Color.Text.inverted) .padding(Design.Spacing.medium) - .background(Color.black.opacity(Design.Opacity.medium)) + .background(Color.AppBackground.base.opacity(Design.Opacity.medium)) .clipShape(.rect(cornerRadius: Design.CornerRadius.medium)) .padding(.bottom, Design.Spacing.xxxLarge) } diff --git a/BusinessCard/Views/Features/Share/ShareCardView.swift b/BusinessCard/Views/Features/Share/ShareCardView.swift index 38f92f5..0c1657d 100644 --- a/BusinessCard/Views/Features/Share/ShareCardView.swift +++ b/BusinessCard/Views/Features/Share/ShareCardView.swift @@ -101,7 +101,7 @@ private struct QRCodeSection: View { QRCodeView(payload: card.vCardPayload) .frame(width: Design.CardSize.qrSizeLarge, height: Design.CardSize.qrSizeLarge) .padding(Design.Spacing.large) - .background(Color.white) + .background(Color.ShareSheet.rowBackground) .clipShape(.rect(cornerRadius: Design.CornerRadius.large)) // Instruction text @@ -146,7 +146,7 @@ private struct AppClipSection: View { QRCodeView(payload: result.appClipURL.absoluteString) .frame(width: Design.CardSize.qrSizeLarge, height: Design.CardSize.qrSizeLarge) .padding(Design.Spacing.large) - .background(Color.white) + .background(Color.ShareSheet.rowBackground) .clipShape(.rect(cornerRadius: Design.CornerRadius.large)) // Expiration notice @@ -190,7 +190,7 @@ private struct AppClipSection: View { if let error = appClipState.errorMessage { Text(error) .typography(.caption) - .foregroundStyle(.red) + .foregroundStyle(Color.Accent.red) .multilineTextAlignment(.center) } } diff --git a/BusinessCard/Views/Shared/Components/AddedContactFieldsView.swift b/BusinessCard/Views/Shared/Components/AddedContactFieldsView.swift index 7e07e99..53a76b5 100644 --- a/BusinessCard/Views/Shared/Components/AddedContactFieldsView.swift +++ b/BusinessCard/Views/Shared/Components/AddedContactFieldsView.swift @@ -4,7 +4,7 @@ import Bedrock /// Displays a vertical list of added contact fields with tap to edit and drag to reorder struct AddedContactFieldsView: View { @Binding var fields: [AddedContactField] - var themeColor: Color = Color(red: 0.2, green: 0.2, blue: 0.2) + var themeColor: Color = Color.Text.secondary let onEdit: (AddedContactField) -> Void @State private var draggingField: AddedContactField? diff --git a/BusinessCard/Views/Shared/Components/ContactFieldPickerView.swift b/BusinessCard/Views/Shared/Components/ContactFieldPickerView.swift index 8801dd6..597722c 100644 --- a/BusinessCard/Views/Shared/Components/ContactFieldPickerView.swift +++ b/BusinessCard/Views/Shared/Components/ContactFieldPickerView.swift @@ -3,7 +3,7 @@ import Bedrock /// Grid view for selecting contact field types to add struct ContactFieldPickerView: View { - var themeColor: Color = Color(red: 0.2, green: 0.2, blue: 0.2) + var themeColor: Color = Color.Text.secondary let onSelect: (ContactFieldType) -> Void private let columns = Array(repeating: GridItem(.flexible(), spacing: Design.Spacing.medium), count: 3) diff --git a/BusinessCard/Views/Shared/Components/FieldRow.swift b/BusinessCard/Views/Shared/Components/FieldRow.swift index 42ed652..90e3b99 100644 --- a/BusinessCard/Views/Shared/Components/FieldRow.swift +++ b/BusinessCard/Views/Shared/Components/FieldRow.swift @@ -21,7 +21,7 @@ struct FieldRow: View { .overlay( field.fieldType.iconImage() .typography(.title3) - .foregroundStyle(.white) + .foregroundStyle(Color.AppText.inverted) ) Button(action: onTap) { diff --git a/BusinessCard/Views/Shared/Components/FieldRowPreview.swift b/BusinessCard/Views/Shared/Components/FieldRowPreview.swift index 4255202..3ce3928 100644 --- a/BusinessCard/Views/Shared/Components/FieldRowPreview.swift +++ b/BusinessCard/Views/Shared/Components/FieldRowPreview.swift @@ -14,7 +14,7 @@ struct FieldRowPreview: View { .overlay( field.fieldType.iconImage() .typography(.title3) - .foregroundStyle(.white) + .foregroundStyle(Color.AppText.inverted) ) VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) { diff --git a/BusinessCard/Views/Shared/Components/FieldTypeButton.swift b/BusinessCard/Views/Shared/Components/FieldTypeButton.swift index c38f735..02727df 100644 --- a/BusinessCard/Views/Shared/Components/FieldTypeButton.swift +++ b/BusinessCard/Views/Shared/Components/FieldTypeButton.swift @@ -15,7 +15,7 @@ struct FieldTypeButton: View { .overlay( fieldType.iconImage() .typography(.title3) - .foregroundStyle(.white) + .foregroundStyle(Color.AppText.inverted) ) Text(fieldType.displayName) diff --git a/BusinessCard/Views/Shared/Components/HeaderLayoutPickerView.swift b/BusinessCard/Views/Shared/Components/HeaderLayoutPickerView.swift index fb672a5..c4723bb 100644 --- a/BusinessCard/Views/Shared/Components/HeaderLayoutPickerView.swift +++ b/BusinessCard/Views/Shared/Components/HeaderLayoutPickerView.swift @@ -89,10 +89,10 @@ struct HeaderLayoutPickerView: View { } label: { Text("Confirm layout") .typography(.heading) - .foregroundStyle(.white) + .foregroundStyle(Color.AppText.inverted) .frame(maxWidth: .infinity) .padding(.vertical, Design.Spacing.large) - .background(.black) + .background(Color.Accent.ink) .clipShape(.rect(cornerRadius: Design.CornerRadius.medium)) } .padding(.horizontal, Design.Spacing.xLarge) diff --git a/BusinessCard/Views/Shared/Components/LayoutBadge.swift b/BusinessCard/Views/Shared/Components/LayoutBadge.swift index 523790b..250bca7 100644 --- a/BusinessCard/Views/Shared/Components/LayoutBadge.swift +++ b/BusinessCard/Views/Shared/Components/LayoutBadge.swift @@ -17,7 +17,7 @@ struct LayoutBadge: View { .padding(.horizontal, Design.Spacing.small) .padding(.vertical, Design.Spacing.xSmall) .background(backgroundColor) - .foregroundStyle(.white) + .foregroundStyle(Color.AppText.inverted) .clipShape(.capsule) } }