byminseok.com

Adding a Dark Mode Toggle to My Blog

Translated from Korean

This blog already had a dark mode. Two weeks ago during refactoring, I added an @media (prefers-color-scheme: dark) block to _variables.scss. The structure was such that when dark mode was enabled on macOS, the blog would darken accordingly. There were two problems.

First, the colors were rough. Background #1a1a1a, border #333, accent #D4735A. Compared to the Terracotta palette for light mode, which was meticulously documented in the design system, the dark side was just "dark for now." It lacked warm tones, making it look like a different blog from the light version.

Second, manual switching didn't work. Since it relied solely on OS settings, users on light mode OS had no way to view this blog in dark mode.

Atelier Dark Palette

We developed a separate dark mode design. Named Atelier Dark, it pairs with the existing light mode ⟨Terracotta⟩ palette.

The core principle is maintaining warm undertones. Instead of pure black #000000, we use #111009⟨almost black but slightly warm⟩; instead of pure white #ffffff, we use #ddd5c0⟨cream tone⟩. The accent color is warm gold #c8a97a instead of terracotta.

Light ⟨Terracotta⟩ Dark ⟨Atelier Dark⟩ Role
#F4F3EE #111009 Background
#E2E0D8 #2a2820 Border
#1A1A1A #ddd5c0 Text
#B1ADA1 #8a7f6e Inactive Text
#C15F3C #c8a97a Accent
#A84E30 #7a6a50 Accent hover

This mapping is organized in dotfiles/_design/atelier-dark-theme.md. It sits alongside the light version documentation⟨terracotta-light-theme.md⟩. This allows the same color scheme to be used in other projects later.

From prefers-color-scheme to data-theme

The limitations of the old approach were clear. CSS @media (prefers-color-scheme: dark) only follows OS settings. It cannot be overridden by JS. Even if a user clicks a toggle button, they cannot change the media query.

The alternative is to add a data-theme attribute to the <html>adding a data-theme attribute to the ` tag.

// 라이트 (기본)
:root {
  --color-bg: #F4F3EE;
  --color-text: #1A1A1A;
  --color-accent: #C15F3C;
}

// 다크
[data-theme="dark"] {
  --color-bg: #111009;
  --color-text: #ddd5c0;
  --color-accent: #c8a97a;
}

[data-theme=&quot;dark&quot;] is a standard CSS attribute selector. In JS, one line—document.documentElement.setAttribute(&#x27;data-theme&#x27;, &#x27;dark&#x27;)—changes all CSS variables. Simple and predictable.

Preventing FOUC

I've encountered FOUC before while building an ECG timeline, and it's even more noticeable in dark mode. That moment when the screen flashes white and then darkens after a refresh. Even if 'dark' is stored in localStorage, if JS sets `</html>