DESIGN

Dark Mode Done Right: Auditing 100+ Files

Andres MuguiraFebruary 26, 20265 min read
Dark ModeCSSTheming
← Back to Blog
Summarize with AI

The Easy Way vs. The Right Way

Most dark mode implementations take the easy way: slap a CSS filter on the root element, invert everything, and call it done. The result looks like a photo negative. White backgrounds become black, but so do images. Brand colors shift. Shadows become glows. Borders disappear against dark backgrounds. It technically works, but it looks like an afterthought — because it is one.

We chose the right way: audit every file, replace every hardcoded color with a CSS custom property, and define dark mode values for every single token. It took a week. It touched 107 files. It was tedious, methodical, and completely unglamorous. But the result is a dark mode that looks like it was designed, not generated.

Dark mode is not the absence of light mode. It is a complete redesign of your color system that happens to use darker values. Treat it as anything less, and your users will notice.

The Audit Process

We started by cataloging every hardcoded color value in the codebase. Not CSS variables, not Tailwind classes — actual hex codes, RGB values, and named colors that were embedded directly in component styles. We found 342 of them across 107 files.

Categorizing the Colors

Each hardcoded color fell into one of five categories:

Side-by-side: the same contacts grid in light mode (left) and dark mode (right)

The Token System

Every hardcoded color was replaced with a CSS custom property. The naming convention follows a simple pattern: --color-purpose-variant. For example, --color-surface-primary for the main background, --color-text-primary for the main text, --color-border-subtle for light borders.

The light mode values are defined on :root. The dark mode values are defined on [data-theme="dark"]. Switching between modes is a single attribute change on the HTML element, and every color in the entire application updates instantly. No JavaScript animation, no flash of unstyled content, no partial rendering with mixed modes.

The Hardest Part: Shadows

Shadows in light mode use dark-with-transparency values like rgba(0, 0, 0, 0.1). On a white background, this creates a subtle shadow. On a dark background, the same shadow is completely invisible because you are adding transparent dark on dark. The common fix is to use light shadows on dark backgrounds, but light shadows look unnatural. They glow instead of recede.

Our solution was to replace most shadows with subtle border changes in dark mode. Cards that had a shadow in light mode get a 1px border in dark mode. Elevated elements like modals and dropdowns keep their shadows but shift to a darker, more opaque value that creates contrast against the dark surface beneath them. The visual hierarchy is maintained through a combination of border lightness and background lightness rather than relying on shadows alone.

The File-by-File Approach

We deliberately chose not to do a global find-and-replace. Each file was opened, read, understood, and updated individually. This was slower, but it caught problems that automation would miss:

Pipeline kanban board in dark mode with teal accents and subtle card borders

Testing Dark Mode

Visual regression testing saved us from shipping a broken dark mode. We capture screenshots of every major view in both light and dark modes, then compare them against baseline images. Any pixel difference above a 0.1% threshold triggers a review. During the dark mode rollout, this caught 14 views where elements were invisible (dark text on dark background), 8 views where contrast ratios were below WCAG AA, and 3 views where images had white borders that looked wrong on dark backgrounds.

The file-by-file approach is boring. It is also the only approach that produces a dark mode worth using. There are no shortcuts in color system design.
Settings page in dark mode with the Appearance panel and theme toggle visible

User Preference Detection

SalesSheet respects the operating system's dark mode preference by default using prefers-color-scheme: dark. If your Mac, iPhone, or Android device is set to dark mode, SalesSheet automatically opens in dark mode. Users can also override this in Settings, choosing between Light, Dark, and System (which follows the OS preference).

The preference is stored locally and applied before the page renders, preventing the flash of light mode that many web apps show when loading in dark mode. We inject a tiny inline script in the document head that reads the stored preference and sets the theme attribute before any CSS is parsed. The script is 12 lines and adds less than 1ms to page load.

What We Shipped

The dark mode audit resulted in a complete, consistent dark mode across every view in SalesSheet: the contact grid, the deal pipeline, the email composer, the AI chat interface, the analytics dashboards, the settings pages, the onboarding flow, and the mobile experience. Every element has been individually considered. Every contrast ratio meets WCAG AA. Every interaction state is visible in both modes.

It took 107 files and a week of focused work. It was worth every minute.

Try SalesSheet Free

No credit card required. Start selling smarter today.

Start Free Trial