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:
- Colors.xcassets – this has our semantic colours
- ColorSystem.json – this has the real colours and their hexes (red-1 etc.)
- ColorMap.json – this has a marrying of the two
- 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.
2
Answers
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:The dark mode values of
red1
,red2
ect you’d define in your assets folder.Now in your code, you just access them as:
Update
The handling of the different colors for the different modes is done in the asset catalogue. In the example below, it is called "Background primary" rather than "red1"; but you can call this whatever.
Either you define the colors in your asset catalogue according to the color profile for the brand (which states Primary, Secondary act act) or you define the colors you use here (e.g. red, yellow, green act) and then set the definitions in your code as explained above.
The first is the "correct" way to do it, since you’ll define categories you reuse in the app. Setting for instance a background to red in light mode and green in dark mode, you’ll select the color "background" in your app, not red or green.
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.