Miloš Jeremić

Product Designer

Color: Back in Black

Yes, I’m into AC/DC. I also try to respect user preferences as much as I can. If you have light or dark mode enabled in your OS, I’d like this website to adhere to your choice.

Two Theme or not Two Theme

That being said, I’ve been giving it some thought: I’m still in the beginning of this site’s redesign, and while it seems logical to implement theme support from the start, having two scales to test every time I change something could easily create an overhead, distract me from current flow, and delay implementing other features. It could become especially complex with the image/asset management, or if I decide to add shadows to UI elements (which I probably will).

To avoid constraints and be able to move faster, I’ll proceed with keeping both themes in mind, creating a separate CSS file where I’ll test them on the go, but one of them won’t be visible nor prioritized, for now at least.

Since I use dark mode almost exclusively across devices, probably my website should be dark, as well?

A New Color Space

I’m going to encode the colors in OKLCH, which is a new color format that came to our browsers with CSS color 4 specification, as an alternative to formats such as HEX or RGB. I believe that it will soon become the standard in modern web development.

It’s expressed in oklch(L C H / a), with the parameters being as follows:

  • L is perceived Lightness, and it ranges from 0 (black) to 1 (white)
  • C is Chroma, the amount of color, from 0 (gray) to most saturated
  • H is Hue angle, and it ranges from 0 to 360
  • a is an optional alpha (transparency) value, ranging from 0 to 1

Lightness and alpha can be expressed in percentages, as well (0%-100%).

Here’s an example:

.div {
	color: oklch(0 0 0); /* black */
	color: oklch(1 0 0); /* white */
	color: oklch(0.5 0.2 30 / 50%); /* transparent red */
}

What are the benefits of OKLCH over RGB or HSL?

RGB stands for “Red, Green, Blue” (Hex being RGB in hexadecimal form), and the three numbers don’t relate to how we perceive the colors, you can not tweak them intuitively.

HSL tries to better that by relying on Hue, Saturation, and Lightness. While being more readable, it’s still not truly perceptual. For example, we perceive yellow saturated at 80% as far more vivid than blue at 80%.

The biggest advantage of OKLCH is perceptual uniformity, meaning that its values correspond to how we actually see the color. Tweaking any parameter results in a predictable and uniform change, while that same tweak in HSL or RGB might result in an unpredicted jump or barely any visible difference. That means that you can freely tweak one of the parameters, without caring about the other two.

It also supports a wider color gamut, but I won’t get much into detail. If you’re interested in the subject, I recommend further reading:

So, OKLCH is more human readable, provides for better accessibility, and is easier to generate palettes with.

Color Palette

I’ll start with a simple 12-step palette, like this:

:root {
  --color-monochrome-01: oklch(0.16 0 0);
  --color-monochrome-02: oklch(0.2 0 0);
  --color-monochrome-03: oklch(0.24 0 0);
  --color-monochrome-04: oklch(0.28 0 0);
  --color-monochrome-05: oklch(0.32 0 0);
  --color-monochrome-06: oklch(0.4 0 0);
  --color-monochrome-07: oklch(0.48 0 0);
  --color-monochrome-08: oklch(0.56 0 0);
  --color-monochrome-09: oklch(0.64 0 0);
  --color-monochrome-10: oklch(0.72 0 0);
  --color-monochrome-11: oklch(0.8 0 0);
  --color-monochrome-12: oklch(0.96 0 0);
}

Now, as much as I like a pure grayscale, it can sometimes appear slightly greenish on displays due to how human vision processes different wavelengths. To avoid that, I’ll be aiming the hue value at a slightly bluish tint, and increase chroma a bit:

:root {
  --color-monochrome-01: oklch(0.16 0.004 256);
  --color-monochrome-02: oklch(0.2 0.004 256);
  --color-monochrome-03: oklch(0.24 0.004 256);
  --color-monochrome-04: oklch(0.28 0.004 256);
  --color-monochrome-05: oklch(0.32 0.004 256);
  --color-monochrome-06: oklch(0.4 0.004 256);
  --color-monochrome-07: oklch(0.48 0.004 256);
  --color-monochrome-08: oklch(0.56 0.004 256);
  --color-monochrome-09: oklch(0.64 0.004 256);
  --color-monochrome-10: oklch(0.72 0.004 256);
  --color-monochrome-11: oklch(0.8 0.004 256);
  --color-monochrome-12: oklch(0.96 0.004 256);
}

The minimal tint helps maintain more consistent appearance across different displays and makes the palette feel more refined, while maintaining the appearance of being “neutral”. This is how it looks like:

Color Palette

Notice how easy it was to achieve this with OKLCH?

Color Usage

This might differ in the future, but in principle I’ll be using color steps this way:

  • 1-3 for backgrounds
  • 3-6 for borders
  • 7-12 for text and icons
:root {
  --color-bg: var(--color-monochrome-01);
  --color-surface-1: var(--color-monochrome-02);
  --color-surface-2: var(--color-monochrome-03);
  --color-border-subtle: var(--color-monochrome-04);
  --color-border-default: var(--color-monochrome-05);
  --color-border-strong: var(--color-monochrome-06);
  --color-text-disabled: var(--color-monochrome-07);
  --color-text-muted: var(--color-monochrome-08);
  --color-text-default: var(--color-monochrome-09);
  --color-text-emphasis: var(--color-monochrome-10);
  --color-text-strong: var(--color-monochrome-11);
  --color-text-highlight: var(--color-monochrome-12);
}

Is twelve steps too much for such a simple site? Probably. I could live without 10 and 11, for sure. But, for now, since I’m not sure where I’m going with it, the flexibility of a nuanced scale provides some peace of mind. I’ll aim to subtract as I go.

Dark Mode: On!

With this, here’s where the site stands at the time of writing:

milosjeremic.com

What do you say, is it time to spice-up the UI next?

Caution! Work In Progress

I'm redesigning this website in the open, and documenting my thoughts and changes as I go:

2025