All Articles

Dark Mode is More Than Just #000: Designing High-Contrast Interfaces

Inverting colors isn't dark mode — it's eye strain with a dark background. Here's how elevation, gray scales, and WCAG ratios combine to make a genuinely accessible dark UI.

When dark mode became a platform-level feature — first on macOS, then iOS, Android, Windows — designers everywhere celebrated. Then most of them implemented it incorrectly.

The most common mistake: inversion. Take the light UI, flip the colors, ship it. The background becomes near-black. The text becomes near-white. Everything is technically "dark." And everything feels slightly wrong, slightly harsh, slightly amateur.

Real dark mode design is about elevation through light, not the absence of it.


The Core Principle: Dark UIs Use Light to Show Depth

In physical space, we understand depth through light. Objects closer to a light source appear brighter. Objects in shadow recede.

Material Design (Google) codified this for digital dark interfaces with a principle called surface elevation: the higher an element sits in the z-axis — the more "on top" it is — the lighter its background should be.

This is counterintuitive but crucial. In a dark UI:

  • The base layer (background) is the darkest
  • Cards sit one level up, so they're slightly lighter
  • Modals sit above cards, so they're lighter still
  • Tooltips, dropdowns, floating elements are the lightest surfaces

You're not changing opacity or adding shadows (shadows barely read on dark backgrounds). You're tinting the surface with white at increasing opacity levels.


Building a Gray Scale That Works

The first thing most designers get wrong is reaching for #000000 or #111111 as their base. Pure black has no room to breathe. It creates extreme contrast that causes eye strain over long sessions.

Here's a functional dark scale built on a warm-gray foundation:

:root[data-theme="dark"] {
  /* Surfaces — from deepest to highest elevation */
  --surface-0: #121212; /* base background */
  --surface-1: #1e1e1e; /* cards, sidebars */
  --surface-2: #252525; /* dropdowns, popovers */
  --surface-3: #2c2c2c; /* modals, overlays */
  --surface-4: #333333; /* tooltips, top-layer UI */
 
  /* Text */
  --text-primary: rgba(255, 255, 255, 0.87);
  --text-secondary: rgba(255, 255, 255, 0.6);
  --text-disabled: rgba(255, 255, 255, 0.38);
 
  /* Dividers and borders */
  --divider: rgba(255, 255, 255, 0.12);
}

Notice that text isn't #FFFFFF. Pure white on near-black creates a contrast ratio above 21:1 — technically accessible but visually exhausting. The rgba(255, 255, 255, 0.87) sweetspot lands around 15:1 contrast — still well above WCAG AA, but far more comfortable.


WCAG Contrast Ratios: The Non-Negotiable Numbers

The Web Content Accessibility Guidelines define minimum contrast requirements:

StandardRatioUse case
WCAG AA (normal text)4.5:1Body text, labels
WCAG AA (large text)3:1Headings ≥ 18px bold or ≥ 24px
WCAG AAA7:1Highest accessibility standard

These aren't suggestions. They're the legal baseline in many jurisdictions and the ethical baseline everywhere else.

A simple rule: if you can't immediately see your text in a screenshot thumbnail, the contrast is too low.

Tools for checking:

  • Colour Contrast Analyser (desktop app, free)
  • Figma's built-in accessibility plugin
  • Chrome DevTools → Accessibility pane (shows contrast ratios live)

The Elevation System in Practice

Let's see how this plays out in a real UI — a notification dropdown:

/* Base page */
body {
  background-color: var(--surface-0); /* #121212 */
}
 
/* Content cards */
.card {
  background-color: var(--surface-1); /* #1E1E1E — slightly elevated */
  border: 1px solid var(--divider);
  border-radius: 12px;
}
 
/* Dropdown menu */
.dropdown {
  background-color: var(--surface-2); /* #252525 — higher elevation */
  border: 1px solid var(--divider);
  border-radius: 8px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5); /* shadows still add depth */
}
 
/* Modal */
.modal {
  background-color: var(--surface-3); /* #2C2C2C */
}

The differences between these values are subtle — about 6-7 hex steps in lightness each time. That's intentional. You're not trying to make the difference obvious; you're trying to make it felt. Users shouldn't notice the elevation system consciously. They should just intuit that the modal is "on top of" the page.


Accent Colors in Dark Mode: Don't Just Reuse Your Light Palette

This is a common trap: you have a brand blue (#1971C2) that works beautifully in light mode. You switch to dark mode and keep it. It now looks harsh and slightly neon against the dark background.

Accent colors in dark mode typically need to be slightly desaturated and lightened. A color that carries weight against white needs to be modified to not overwhelm a dark background.

Compare:

/* Light mode accent */
--accent: #1971c2;
 
/* Dark mode accent — lighter, slightly less saturated */
--accent: #74c0fc;

The dark mode accent is from the same blue family, but several steps lighter. It reads as active and intentional without screaming.

This applies to every brand color. Test each one on both backgrounds. They will almost never work identically on both.


Practical Checklist

Before shipping a dark mode interface, verify:

  • Base background is #121212#1A1A1A, not #000000
  • Each elevation layer is 5–8 lightness steps above the previous one
  • Text contrast ratios: ≥ 4.5:1 for body, ≥ 3:1 for large text
  • No pure #FFFFFF text — use rgba(255,255,255,0.87) or equivalent
  • Accent colors are lightened and tested against dark surfaces
  • Shadows are darker and more opaque (they don't read well at low opacity on dark)
  • Borders use rgba(255,255,255,0.12) rather than dark-on-dark attempts
  • You've tested in dim lighting conditions, not just a bright studio monitor

Dark mode done right isn't dark at all — it's a carefully controlled family of near-blacks, each carrying meaning about depth and hierarchy.

The difference between professional dark UI and amateur dark UI is not the darkness. It's the light.

Share
Enjoyed this article?

Check out what I've built

From Roblox games with millions of visits to open-source developer tools — see everything on my portfolio.

View Portfolio