Home > Software engineering >  The best way to manage colours in dark or light mode
The best way to manage colours in dark or light mode

Time:02-23

At work we have a design system for our colours, for example

red-1 red-2 red-3

green-1 green-2 green-3

The hex codes behind these might change, but also a component or set of components might, for example, change their colour from red-1 to red-2.

We also want to handle dark mode. In light mode you might have a background as red-1, but in dark mode it needs to be green-1. This means we might want semantic colours as well, like:

background-1 (red-1 in light mode, green-1 in dark mode) foreground-1

etc.

I know that the simplest way to handle dark mode with the least boilerplate is to use Asset catalogues. So you'd have a color asset called background-1, and that would have a dark and light mode color.

The problem is, that color can't reference other custom colors (like red-1). Instead you have to manually put in the hex code, meaning if red-1 changes, you've got a real headache on your hands in changing all the semantic colors that use red-1.

On the other hand, as far as I can see, if you go down the programmatic route (i.e. use an asset catalogue for your red-1, green-1, and a class/enum for your semantic colors) you need to add a ton of boilerplate to each class that uses these colors (traitCollectionDidChange, I think?).

Can anyone think of a happy medium here? Happy to use external tools if one exists.

EDIT: One idea I had:

  1. Colors.xcassets - this has our semantic colours
  2. ColorSystem.json - this has the real colours and their hexes (red-1 etc.)
  3. ColorMap.json - this has a marrying of the two
  4. Build phase script to take ColorSystem.json and ColorMap.json and marry the two to generate a fresh Colors.xcassets system.

Then whenever we change a hex, we change ColorMap.json. Whenever we change a semantic color to use a different "real" color, we changed ColorMap.json.

CodePudding user response:

Maybe I misunderstand your exact question, but how about you add your types of colors to your assets. These are often defined as "Primary" "Secondary" ect. but you could add them as "red1", "red2" ect.

Then in code, you could set these assets up in an UIColor extension, such as:

extension UIColor {
    // MARK: Colors set in assets folder
    static var red1: UIColor {
        UIColor(named: "red1") ?? .red // Named returns an optional, so default
    }

    static var red2: UIColor {
        UIColor(named: "red1") ?? .red // Named returns an optional, so default
    }

    static var black1: UIColor {
        UIColor(named: "black1") ?? .black
    }

    // MARK: - My predefined colors
    static var primaryBackground: UIColor {
        .red1
    }

    static var primaryText: UIColor {
        .black1
    }

    static var highlightedText: UIColor {
        .red1
    }

    static var secondaryBackground: UIColor {
        .red2
    }
}

The dark mode values of red1, red2 ect you'd define in your assets folder.

Now in your code, you just access them as:

override func viewDidLoad() {
  super.viewDidLoad()
  view.backgroundColor = .primaryBackground
}

CodePudding user response:

Do what the asset catalog does, without actually using the asset catalog. Define your colors entirely in code as light / dark mode pairs using the UIColor dynamic provider initializer:

https://developer.apple.com/documentation/uikit/uicolor/3238041-init

Now just go ahead and use those colors throughout the app. No need to know whether you're in light or dark mode. No "overhead" required. Your colors will automatically be always correct for the current mode, and will change automatically when the user changes between light and dark modes.

  • Related