Skip to content
Consultation

My Accent Color Looked Great in Dark Mode, Then Died in Light

Adityo Guni Waluyo

Orange looked great in dark mode but washed out in light. A split accent, emerald-600 in light and mint in dark, clears the WCAG contrast bar in both.

The blog looked done. Dark mode felt right, tag chips rounded and glowing, filter nav underlined in that Hacker News orange #FF570A I'd picked for the link-aggregator vibe, search focus ring pulsing the same shade. Then I flipped the toggle to light mode and the whole sidebar went pale. Active badges in BlogSidebar.tsx looked like a phone screen viewed in direct sunlight: technically there, visually gone.

First guess was my monitor. Or maybe just tired eyes at 1am after too many deploys. I nudged the brightness, squinted, restarted the dev server, even opened it on my laptop instead of the external display. Same washout. It wasn't hardware. It was math.

Why the orange had to go

I'd built the blog with a default dark mode plus a light mode, because some readers actually use light. Orange on the dark background #0d1117 sat at 6.24:1, comfortable. But on white it dropped to 3.17:1. Readable-ish for big bold text, weak for the smaller active chip states. Once light mode existed as a real option, the accent that tied the UI together quietly fell apart in half the themes.

So I switched the whole accent to CodeRabbit mint #25E2A8. Looked killer in dark. Then I ran the WCAG relative-luminance calc for light mode and stopped smiling: #25E2A8 on white lands at only 1.68:1. That fails even the 3:1 non-text contrast floor from W3C WCAG 2.2 Understanding Contrast Minimum. Mint on white is basically invisible, worse than the orange ever was.

The fix is a split accent. Light mode uses Tailwind's emerald-600 (#059669) behind the mint, giving 3.77:1 against the mint text. Dark mode keeps pure #25E2A8 on #0d1117 at 11.29:1, crisp as ever.

Per the spec, normal text needs min 4.5:1 (SC 1.4.3); large text and non-text UI need only 3:1. So emerald-600 at 3.77:1 passes for bold small badges and nav items, not for body copy. I keep mint strictly for UI chrome in BlogArchive.tsx year labels and sidebar filters, never paragraphs.

One const, two modes

Tailwind's dark: variant does the heavy lifting. I collapsed every accent reference in BlogSidebar.tsx and BlogArchive.tsx into a single shared constant so the nav, badge, and focus ring all inherit the same rule. Arbitrary hex values like text-[#25E2A8] work straight from Tailwind colors, and the dark: prefix handles per-mode switching per Tailwind dark mode.

// Before: one accent for all modes, fails in light
const ACCENT = "text-[#FF570A]";
const ACCENT_BG = "bg-[#FF570A]/10";

// After: emerald-600 in light, mint in dark
const ACCENT = "text-emerald-600 dark:text-[#25E2A8]";
const ACCENT_BG = "bg-[#25E2A8]/10";

Every component imports ACCENT and ACCENT_BG. The active filter state, the archive year badge, the search input focus ring, all just spread the string. No per-component color logic, no theme branching in JSX, no duplicate classes to drift out of sync later. The diff for a16f470 touched exactly those two files and nothing else, small change, large readability win.

I'll state my position plainly: an accent color is not a personality decision you make once in a dark screenshot. If your app has more than one theme, the accent has to clear the contrast bar in every one of them, or it's not a brand color, it's a bug you haven't noticed yet. Most people would keep orange for the vibe and ship it. I'd rather have a sidebar I can actually read at noon.

The real lesson landed after the commit: brand color isn't just taste. An accent that survives one background may die on another. Pick once, test both modes before you call the UI finished.

Related articles