Fantomas, John Snow, and Mabel Pines. How we did the dark mode for ispmanager
Hi there! My name is Sergei. I'm a Product Designer in ispmanager team. Today I'm going to tell you how we did the dark mode for ispmanager. Make yourselves comfortable! Although the job had seemed easy, we found that it wasn't that at all. So it's a kind of a long story, and pretty fascinating. Let's start from the beginning!
Why did we even think of adding a dark mode? Long story short, we just wanted to do something nice for ispmanager users, and refresh the product's UI in the process. The selection of a dark theme is not always due to aesthetic considerations. For PC users who work at night, the dark mode is a necessity rather than a whim.
At first glance, the obvious solution would be to tweak the existing palette by pairing up each color with its dark doppelganger. This quite possibly would have worked for a new and trim product in which everything is uniform, economical, and done by the book from the get-go.
But ispmanager is a product with a solid history. For a long time, it had gone without any clearly formulated color rules or standardized components. There was the logical possibility that each of the fairly many people having contributed to the project over its 18-year evolution could have left some kind of mark on it.
Data collection
They say that the theater begins with a hanger. And the color scheme of an app starts with the palette. First of all, we decided to compare the actual palette with the one prescribed in our design system, and counting how many color shades were available for use.
In the worst case scenario, I figured, we would have 30 to 40 color shades in use: a few blues and a few grays. Red, yellow, and green... Let me tell you right now: even my boldest projections were exceedingly far from the truth.
Our Frontend Developer - Borislav and I painstakingly analyzed the use cases of every color shade. We had to check, literally, every nook and cranny of the product to make sure we had not overlooked anything. As a result, it turned out the following:
- The palette in our design system was found to be significantly different from the physical palette.
- Some colors had "doubles" that were perfectly useless.
- Naming of colors is uninformative and requires improvement.
- There was an urgent need to unify components.
We counted close to 100 colors in the final document. As you can see, there was plenty of room for optimization. Interfaces with rigorous procedure and uniformity always look a lot better. Moreover, applications suffering from such a degree of neglect add significantly to development costs. The designer has to think in terms of man/hours and search for optimal solutions to the tasks at hand. All the changes we made while working on the dark mode will cut the input of man/hours needed to update the UI going forward.
For convenience, I broke the target palette down in Figma, and scripted the use ambit for each color. Before I could start the optimization process, I had to answer a set of questions for each group of "doubles":
- Is it okay to merge two like colors into one case by case?
- What would be the best way to merge colors? Which shade would fit better?
- What would be the easiest and most universal names for the resultant colors?
The tasks where transparency was one of the color's attributes are marked with asterisks.
New color selection and naming
Having organized my current palette, I put together another test stand where I grouped the usable colors under two categories: "can be removed" or "cannot be removed". I used yellow labels as reminders and pointers showing how things were to be moved. This is what my stand looked like:
My idea was to shrink the palette to the smallest size sufficient for the product to remain usable. I checked each color and shade manually. Locating the component where the suspected double was used, I tried replacing it with the new, uniformized shade. Looks good? Then remove it!
Here is what my palette looked like after a while:
Looks much better, doesn't it? I had culled some 100 color shades down to just 15 to 20 essential ones. The only thing that catches your eye was the suboptimal naming left over from legacy. It had to be completely reworked to bring the names to a common denominator. Now what was a good way to do that?
The product originally had a weird color ID system. Here are the shades of yellow color, for instance: isp-yellow-3, isp-yellow-7, isp-yellow-8, isp-yellow-9, isp-yellow-6, isp-yellow-11. What happened to the missing ordinal numbers? It's a mystery!
If you look at Google Material, everything is perfect there. The colors are compartmentalized by shade, and each progresses smoothly from light to dark. The numbers are all there - nothing is missing. We had to figure out a new principle of color identification, and change the principles of color shade creation while we were at it.
- Numbering? It would not give us enough flexibility. What do we do when we need the color residing in between Blue No. 5 and Blue No. 6?
- sRGB or X11 standard? Not universal enough and not so user-friendly. We would have to add lots of unnecessary color shades. We also wanted a more customized system.
What about an exclusive "brand" name for every color shade in our new palette? One that the front end team will think is cool and one that's easy to remember? Perfect!
This part of our work was probably the coolest and the most fun! Characters like Jon Snow, Fantomas, Nosferatu, Malewicz, and many other colorful celebrities came to stay in our design system. It's easier to work on styles with a palette like this: over time, developers get to identify color shades with the character names.
I split the palette into "master", "secondary", and "extended" color shades. I also had to tweak the basic colors. Texts in red, for instance, looked totally infernal even in the light mode. Here is the gradation I got for the shades of blue:
Important point! All interface colors have to be contrast-checked on WCAG. Figma plugins Contrast and Color Blind will help with this.
As for the shades of gray, they had been all over the place before. Some gravitated to green, others to blue. I got rid of all color tones, ending up with an entirely achromatic range. The gray shades have only one attribute left: lightness.
Now let me explain how we add each of the new color. A new color shade is derived by changing the density and lightness values by an integer divisible into 5. This is our guarantee that the palette will remain homogeneous and that developers won't have to improvise colors: they can simply compute the right shade from the basic color as necessary. The front end team agreed to never use "raw" values again when developing components: they would only use variables with color instead (it's okay to use such values to determine the color variable, but not in any other case).
And to make sure the palette is as fun to use as it is user friendly, we filled it to the brim with cultural references, including films, video games, famous figures, and just fun names like "hang over".
Interface points
As we know from the writings of Jakob Nielsen, all parts of an interface must connect with the real world. All buttons must be clickable, the important parts must be visually closer to the viewer than secondary parts, and so on.
Ispmanager has plenty of pop-up menus, drop-down lists, selection options, and other elements that need to be highlighted. Check out the screenshot below:
The objects in front have drop shadows. They are there for a reason. Shadows have the visual effect of "lifting" the object up a little, gently highlighting it versus the ambience. This is not for embellishment, but to assist the user with product navigation.
A drop shadow will not work with the dark mode though. You'd think that the shadow could be inverted, made light, but trust me: it looks hideous. Outlining was chosen as a partial solution. Look at this screenshot:
The outlining lets us see the object's contour. There is outlining in the light mode, too. It's light gray and almost imperceptible, but it makes a big difference in low-contrast displays. We thought we'd keep the drop shadows as well. The dark mode, naturally, makes them disappear but not 100%, because our background color is not entirely black.
When we added the outlines, another nuance revealed itself: the interface lacked "depth" and forms got mixed up when the select feature was open. We dealt with this by "painting" all modal windows, lists and notifications a lighter tone, making the closer objects lighter, and the more distant ones darker.
Dynamic colors
The first idea when developing a dark theme was to replace the entire palette with a "dark" one. But we could see right away there would be issues with that, with tree dependence being the most salient one. Let me explain what this means.
Let's imagine that the entire application consists of four variables for color.
For some reason, the designer decides to replace obsidian with flower, and flower with sun in the dark mode. He left the other colors alone. Now this is what the dark mode colors will look like:
Now obsidian has the same value as sun, for it has become dependent on the flower color.
Our next thought was to identify the variables by components and assign colors to them. But we soon gave it up because of the huge number of components involved.
Having reviewed other companies' cases and design systems, the team agreed on a solution involving the use of color tokens. The problem with this solution was the time it would take to implement. We had to examine 600+ color locations and merge some of them on the basis of their shared use areas.
In other words, we had to come up with a method that would allow us to change some project colors without having to analyze them all. So we came up with the dynamic colors idea.
Every dark mode color has its negative "twin", available from the static color palette. Thus Malewicz gets repainted in Gandalf Gray. At creation, each component gets programmed with a light/dark color pair as a single attribute.
The same principle works for text features, links and icons. For example, a link text is regularly one color, but will change color when the cursor gets pointed at it. You only need to specify the dynamic color code instead of colors A and B.
In the dynamic palette, each color will change with the current mode. For example, --isp-dc-cheese (the prefix dc is for dynamic color) will be light yellow in the light mode, and dark brown in the dark mode. With this solution in place, the current color palette can be left alone entirely. It is sufficient to change static color to dynamic in the component. Where the color doesn't change with the mode, it's okay to simply use static color.
Results and plans for the future
For now, I can say that the dark mode looks 90% of how we had wanted it to look. The palette and the component recoloring action were tweaked continually during the entire development and testing process, and yet some quirks have persisted. Sometimes there isn't enough contrast, also in the light mode. Icons are their own subject that should be dealt with in a separate small article. My task happens to be a bit more global than making the dark mode work: I'm working to raise the app's design level across the board. Over time, this will be done and rolled out.
In the meantime, here is a curious point. Remember! All dark mode prototypes are to be tested in a dark room. I've been through this myself: I had prototyped in Figma and tested my prototype by daylight. When I opened the prototype at night I discovered I had missed the mark with the gray shades: they had to be darker. Actually, a colleague of mine has already covered this. Her article is recommended.
Dark mode development turned out to be so much more of a challenge than we had expected or could have predicted at a glance. That said, we went beyond dark mode implementation per se in this project to introduce a bunch of product design improvements, and namely:
- We improved the light mode while developing the dark one.
- We contributed towards a better user experience by improving detail resolution of the interface.
- We uniformized the components and worked out a system.
- We cut UI updating costs going forward.
The current edition of the dark mode is in for further fine-tuning. Meanwhile, we have been working on yet another UI component: the cards. They will address two needs at once:
- Alternative, more clear and beginner-friendly rendering of the lists.
- Gaps filled, when the user has added just one website.
There's plenty of work ahead. New features keep getting released and are continually elaborated. We have a project roadmap to show for it, too. Some new releases in our pipeline include: Docker support, Drag’n’Drop, new downloading center, notification center, and widgets for homepage. These are some of the additions our users had requested. They are coming soonwhere everyone will be able to see them.