Skip to content

Commit

Permalink
feat: 🎸 improve color palette
Browse files Browse the repository at this point in the history
  • Loading branch information
justinguo authored and MarcoEidinger committed Aug 31, 2020
1 parent 65ddaea commit d6a8837
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 88 deletions.
100 changes: 56 additions & 44 deletions Sources/FioriSwiftUICore/ColorPalette/Colors/HexColor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,24 +58,12 @@ public struct HexColor: Hashable {
}
}

/// Returns a `Color` that matches with the specified background color scheme from `HexColor`.
///
/// - parameter colorScheme: specifies the background color scheme. Defaults to `.light`.
/// - Returns: `Color`
public func color(_ colorScheme: ColorScheme = .light) -> Color {
let (r, g, b, a) = rgba(colorScheme)
return Color(.sRGB, red: r, green: g, blue: b, opacity: a)
}

/// Returns a RGBA values tuple that matches with the specified background color scheme from `HexColor`.
/// Returns a RGBA values tuple that matches with the specified hex color string.
///
/// - parameters:
/// - colorScheme: specifies the background color scheme. Defaults to `.light`.
/// - interfaceLevel: specifies the level of user interface. Defaults to `.base`.
/// - hexString: specifies the hex color string.
/// - Returns: a tuple of RGBA values for corresponding `HexColor`.
public func rgba(_ colorScheme: ColorScheme = .light, _ interfaceLevel: UIUserInterfaceLevel = .base, _ displayMode: ColorDisplayMode = .normal) -> (r: Double, g: Double, b: Double, a: Double) {
let hexString = hex(colorScheme, interfaceLevel, displayMode)

public func rgba(_ hexString: String) -> (r: Double, g: Double, b: Double, a: Double) {
let hex = hexString.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
var int: UInt64 = 0
Scanner(string: hex).scanHexInt64(&int)
Expand All @@ -96,44 +84,68 @@ public struct HexColor: Hashable {
a: Double(a) / 255)
}

/// :nodoc:
private func hex(_ colorScheme: ColorScheme = .light, _ interfaceLevel: UIUserInterfaceLevel = .base, _ displayMode: ColorDisplayMode = .normal) -> String {
let colorVariant: ColorVariant = colorScheme == .light ? .dark : .light

switch (colorVariant, interfaceLevel, displayMode) {
case (_, .base, _):
if let hex = colors[colorVariant] {
return hex
}
case (.light, .elevated, _):
if let hex = colors[.elevatedLight] {
return hex
/// Returns the string value that matches with the specified color variant from `HexColor`.
///
/// - parameters:
/// - variant: specifies the color variant.
/// - Returns: the string value for corresponding `HexColor` with specific color variant.
public func hex(_ variant: ColorVariant) -> String {
return colors[variant] ?? "FFFFFFFF"
}

/// Returns the `ColorVariant` that matches with the specified combination of background color scheme, user interface level and display mode settings.
///
/// - parameters:
/// - background: specifies the background color scheme, default is `.device`.
/// - interface: specifies the user interface level, default is `.device`.
/// - display: specifies the display mode, default is `.normal`.
/// - Returns: the string value for corresponding `HexColor` with specific color variant.
public func getVariant(background scheme: BackgroundColorScheme? = .device, interface level: InterfaceLevel? = .device, display mode: ColorDisplayMode? = .normal) -> ColorVariant {
var variant = ColorVariant.dark
let traits = UIView().traitCollection
let isDarkInterfaceStyle = traits.userInterfaceStyle == .dark
let style: UIUserInterfaceStyle = {
switch (scheme ?? .device, isDarkInterfaceStyle) {
case (.lightConstant, _), (.deviceInverse, true), (.device, false):
return .light
case (.darkConstant, _), (.deviceInverse, false), (.device, true):
return .dark
}
case (.dark, .elevated, _):
if let hex = colors[.elevatedDark] {
return hex
}()
let isElevatedInterfaceLevel = traits.userInterfaceLevel == .elevated
let level: UIUserInterfaceLevel = {
switch (level ?? .device, isElevatedInterfaceLevel) {
case (.baseConstant, _), (.deviceInverse, true), (.device, false):
return .base
case (.elevatedConstant, _), (.deviceInverse, false), (.device, true):
return .elevated
}
}()
switch (style, level, mode) {
case (.light, .base, .normal):
variant = .dark
case (.dark, .base, .normal):
variant = .light
case (.light, .elevated, .normal):
variant = .elevatedDark
case (.dark, .elevated, .normal):
variant = .elevatedLight
case (.light, _, .contrast):
if let hex = colors[.contrastLight] {
return hex
}
variant = .contrastDark
case (.dark, _, .contrast):
if let hex = colors[.contrastDark] {
return hex
}
variant = .contrastLight
default:
return "FFFFFFFF"
break
}

return "FFFFFFFF"
return variant
}
}

extension HexColor: Equatable {
/// :nodoc:
public static func == (lhs: HexColor, rhs: HexColor) -> Bool {
return lhs.hex(.light) == rhs.hex(.light) &&
lhs.hex(.dark) == rhs.hex(.dark) && lhs.hex(.light, .elevated) == rhs.hex(.light, .elevated) && lhs.hex(.dark, .elevated) == rhs.hex(.dark, .elevated) && lhs.hex(.light, .unspecified, .contrast) == rhs.hex(.light, .unspecified, .contrast) && lhs.hex(.dark, .unspecified, .contrast) == rhs.hex(.dark, .unspecified, .contrast)
lhs.hex(.dark) == rhs.hex(.dark) && lhs.hex(.elevatedLight) == rhs.hex(.elevatedLight) && lhs.hex(.elevatedDark) == rhs.hex(.elevatedDark) && lhs.hex(.contrastLight) == rhs.hex(.contrastLight) && lhs.hex(.contrastDark) == rhs.hex(.contrastDark)
}
}

Expand All @@ -142,10 +154,10 @@ extension HexColor: CustomStringConvertible {
public var description: String {
return """
{"HexColor": {"base light": "\(hex(.light))", "base dark": "\(hex(.dark))",
"elevated light": "\(hex(.light, .elevated))",
"elevated dark": "\(hex(.dark, .elevated))",
"contrast light": "\(hex(.light, .unspecified, .contrast))",
"contrast dark": "\(hex(.dark, .unspecified, .contrast))",
"elevated light": "\(hex(.elevatedLight))",
"elevated dark": "\(hex(.elevatedDark))",
"contrast light": "\(hex(.contrastLight))",
"contrast dark": "\(hex(.contrastDark))",
}}
"""
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/FioriSwiftUICore/ColorPalette/Palette.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public struct Palette: PaletteProvider {
}

/// :nodoc:
init(_ palette: PaletteProvider) {
public init(_ palette: PaletteProvider) {
self._palette = palette
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ struct PaletteV4: PaletteProvider {
func hexColor(for colorStyle: ColorStyle) -> HexColor {
switch colorStyle {
case .primary1:
return HexColor(lightColor: "FFFFFF", darkColor: "32636A")
return HexColor(lightColor: "FFFFFF", darkColor: "32363A")
case .primary2:
return HexColor(lightColor: "FFFFFF", darkColor: "515559")
case .primary3:
Expand Down
32 changes: 16 additions & 16 deletions Sources/FioriSwiftUICore/ColorPalette/Palettes/PaletteV5.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct PaletteV5: PaletteProvider {
case .separator:
return HexColor(lightColor: "8696A945", darkColor: "89919A69")
case .primary1:
return HexColor(lightColor: "FFFFFF", darkColor: "32636A")
return HexColor(lightColor: "FAFAFA", darkColor: "32363A")
case .primary2:
return HexColor(lightColor: "EEF0F1", darkColor: "515456")
case .primary3:
Expand Down Expand Up @@ -74,13 +74,13 @@ struct PaletteV5: PaletteProvider {
case .quarternaryLabel:
return HexColor(lightColor: "FAFAFA8C", darkColor: "32363A8C", contrastLightColor: "FAFAFA8C", contrastDarkColor: "FAFAFA8C")
case .primaryFill:
return HexColor(lightColor: "89919A33", darkColor: "8696A959", contrastLightColor: "8696A959", contrastDarkColor: "8696A959")
return HexColor(lightColor: "8696A959", darkColor: "89919A33", contrastLightColor: "8696A959", contrastDarkColor: "8696A959")
case .secondaryFill:
return HexColor(lightColor: "89919A29", darkColor: "8696A945", contrastLightColor: "8696A945", contrastDarkColor: "8696A945")
return HexColor(lightColor: "8696A945", darkColor: "89919A29", contrastLightColor: "8696A945", contrastDarkColor: "8696A945")
case .tertiaryFill:
return HexColor(lightColor: "89919A1F", darkColor: "8696A930", contrastLightColor: "8696A930", contrastDarkColor: "8696A930")
return HexColor(lightColor: "8696A930", darkColor: "89919A1F", contrastLightColor: "8696A930", contrastDarkColor: "8696A930")
case .quarternaryFill:
return HexColor(lightColor: "89919A14", darkColor: "8696A91C", contrastLightColor: "8696A91C", contrastDarkColor: "8696A91C")
return HexColor(lightColor: "8696A91C", darkColor: "89919A14", contrastLightColor: "8696A91C", contrastDarkColor: "8696A91C")
case .header:
return HexColor(lightColor: "2C3D4F", darkColor: "354A5F")
case .footer:
Expand All @@ -90,7 +90,7 @@ struct PaletteV5: PaletteProvider {
case .cellBackgroundTapState:
return HexColor(lightColor: "8696A91C", darkColor: "89919A1C")
case .tintColor:
return HexColor(lightColor: "D1E8FF", darkColor: "0A6ED1")
return HexColor(lightColor: "91C8F6", darkColor: "0A6ED1", contrastLightColor: "91C8F6", contrastDarkColor: "91C8F6")
case .tintColorLight:
return HexColor(lightColor: "0A84FF", darkColor: "D1E8FF")
case .tintColorDark:
Expand Down Expand Up @@ -150,25 +150,25 @@ struct PaletteV5: PaletteProvider {
case .esriEdit:
return HexColor(lightColor: "FFF114", darkColor: "1B6DD2")
case .negative:
return HexColor(lightColor: "FF453A", darkColor: "BB0000")
return HexColor(lightColor: "FF8888", darkColor: "BB0000")
case .positive:
return HexColor(lightColor: "32D74B", darkColor: "107E3E")
return HexColor(lightColor: "ABE2AB", darkColor: "107E3E")
case .critical:
return HexColor(lightColor: "FF9F0A", darkColor: "E9730C")
return HexColor(lightColor: "FABD64", darkColor: "E9730C")
case .negativeLabel:
return HexColor(lightColor: "FF8888", darkColor: "BB0000", contrastLightColor: "FF8888")
return HexColor(lightColor: "FF8888", darkColor: "BB0000", contrastDarkColor: "FF8888")
case .positiveLabel:
return HexColor(lightColor: "ABE2AB", darkColor: "07E3E", contrastLightColor: "ABE2AB")
return HexColor(lightColor: "ABE2AB", darkColor: "107E3E", contrastDarkColor: "ABE2AB")
case .criticalLabel:
return HexColor(lightColor: "FABD64", darkColor: "E9730C", contrastLightColor: "FABD64")
return HexColor(lightColor: "FABD64", darkColor: "E9730C", contrastDarkColor: "FABD64")
case .negativeBackground:
return HexColor(lightColor: "FF888824", darkColor: "BB000014", contrastLightColor: "FF888824", contrastDarkColor: "BB000024")
return HexColor(lightColor: "FF888824", darkColor: "BB000014", contrastDarkColor: "BB000024")
case .positiveBackground:
return HexColor(lightColor: "ABE2AB24", darkColor: "107E3E14", contrastLightColor: "ABE2AB24", contrastDarkColor: "107E3E24")
return HexColor(lightColor: "ABE2AB24", darkColor: "107E3E14", contrastDarkColor: "107E3E24")
case .criticalBackground:
return HexColor(lightColor: "FABD6424", darkColor: "E9730C14", contrastLightColor: "FABD6424", contrastDarkColor: "E9730C24")
return HexColor(lightColor: "FABD6424", darkColor: "E9730C14", contrastDarkColor: "E9730C24")
case .informationBackground:
return HexColor(lightColor: "91C8F624", darkColor: "0A6ED114", contrastLightColor: "91C8F624", contrastDarkColor: "0A6ED124")
return HexColor(lightColor: "91C8F624", darkColor: "0A6ED114", contrastDarkColor: "0A6ED124")
case .accent1:
return HexColor(lightColor: "E38B16", darkColor: "E38B16")
case .accent1b:
Expand Down
27 changes: 5 additions & 22 deletions Sources/FioriSwiftUICore/ColorPalette/ThemeManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,28 +44,11 @@ public class ThemeManager {
/// :nodoc:
internal func color(for style: ColorStyle, background scheme: BackgroundColorScheme?, interface level: InterfaceLevel?, display mode: ColorDisplayMode?) -> Color {
let uiColor: UIColor = UIColor { [unowned self] traitCollection in
func getPaletteColor(_ variant: ColorVariant) -> UIColor {
let scheme: ColorScheme = variant == .light ? .dark : .light
let isElevatedInterfaceLevel = traitCollection.userInterfaceLevel == .elevated
let level: UIUserInterfaceLevel = {
switch (level ?? .device, isElevatedInterfaceLevel) {
case (.baseConstant, _), (.deviceInverse, true), (.device, false):
return UIUserInterfaceLevel.base
case (.elevatedConstant, _), (.deviceInverse, false), (.device, true):
return UIUserInterfaceLevel.elevated
}
}()
let components = self.palette.hexColor(for: style).rgba(scheme, level, mode ?? .normal)
return UIColor.init(red: CGFloat(components.r), green: CGFloat(components.g),
blue: CGFloat(components.b), alpha: CGFloat(components.a))
}
let isDarkInterfaceStyle = traitCollection.userInterfaceStyle == .dark
switch (scheme ?? .device, isDarkInterfaceStyle) {
case (.lightConstant, _), (.deviceInverse, true), (.device, false):
return getPaletteColor(.dark)
case (.darkConstant, _), (.deviceInverse, false), (.device, true):
return getPaletteColor(.light)
}
let variant = self.palette.hexColor(for: style).getVariant(background: scheme, interface: level, display: mode)
let hexColorString = self.palette.hexColor(for: style).hex(variant)
let components = self.palette.hexColor(for: style).rgba(hexColorString)
return UIColor(red: CGFloat(components.r), green: CGFloat(components.g),
blue: CGFloat(components.b), alpha: CGFloat(components.a))
}
return Color(uiColor)
}
Expand Down
16 changes: 12 additions & 4 deletions Tests/FioriSwiftUITests/FioriCharts/Model/HexColorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,18 @@ class HexColorTests: XCTestCase {
let color = HexColor()

// Light foreground color should be initialized using .dark color scheme
let lightForegroundColor = color.rgba(.dark)
let lightVariant = color.getVariant(background: .darkConstant, interface: .baseConstant, display: .normal)
let lightColorString = color.hex(lightVariant)
let lightForegroundColor = color.rgba(lightColorString)
XCTAssertEqual(Int(255 * lightForegroundColor.r), 0x0)
XCTAssertEqual(Int(255 * lightForegroundColor.g), 0x0)
XCTAssertEqual(Int(255 * lightForegroundColor.b), 0x0)
XCTAssertEqual(Int(255 * lightForegroundColor.a), 0xff)

// Dark foreground color should be initialized using .dark color scheme
let darkForegroundColor = color.rgba(.light)
let darkVariant = color.getVariant(background: .lightConstant, interface: .baseConstant, display: .normal)
let darkColorString = color.hex(darkVariant)
let darkForegroundColor = color.rgba(darkColorString)
XCTAssertEqual(Int(255 * darkForegroundColor.r), 0xff)
XCTAssertEqual(Int(255 * darkForegroundColor.g), 0xff)
XCTAssertEqual(Int(255 * darkForegroundColor.b), 0xff)
Expand All @@ -46,13 +50,17 @@ class HexColorTests: XCTestCase {
// - For .dark color scheme, should return the light variant of a HexColor
let color = HexColor(lightColor: "12345678", darkColor: "abCDef")

let lightForegroundColor = color.rgba(.dark)
let lightVariant = color.getVariant(background: .darkConstant, interface: .baseConstant, display: .normal)
let lightColorString = color.hex(lightVariant)
let lightForegroundColor = color.rgba(lightColorString)
XCTAssertEqual(Int(255 * lightForegroundColor.r), 0x12)
XCTAssertEqual(Int(255 * lightForegroundColor.g), 0x34)
XCTAssertEqual(Int(255 * lightForegroundColor.b), 0x56)
XCTAssertEqual(Int(255 * lightForegroundColor.a), 0x78)

let darkForegroundColor = color.rgba(.light)
let darkVariant = color.getVariant(background: .lightConstant, interface: .baseConstant, display: .normal)
let darkColorString = color.hex(darkVariant)
let darkForegroundColor = color.rgba(darkColorString)
XCTAssertEqual(Int(255 * darkForegroundColor.r), 0xab)
XCTAssertEqual(Int(255 * darkForegroundColor.g), 0xcd)
XCTAssertEqual(Int(255 * darkForegroundColor.b), 0xef)
Expand Down

0 comments on commit d6a8837

Please sign in to comment.