All posts
Product design

Dark Mode for SaaS Apps: How to Design and Build It Properly

Dark mode UI design for SaaS apps: semantic color tokens, Figma variable modes, CSS variables and contrast checks, so the theme is built once, not duplicated.

By Mehdi Hassan7 min read

The short answer

Dark mode UI design for a SaaS app works best when every color is a semantic token, such as surface, text or border, with a light and a dark value, defined once in Figma variable modes and mirrored as CSS variables. Use dark grays rather than pure black, express elevation with lighter surfaces, and recheck contrast in both themes.

Key takeaways

  • Build dark mode with semantic tokens and modes, never by duplicating screens or inverting colors.
  • Name tokens by role (surface, text-muted, border) not by value (gray-900).
  • Use dark grays for backgrounds and lighter surfaces for elevation instead of heavy shadows.
  • Desaturate and lighten brand and status colors for dark backgrounds, then recheck contrast.
  • Keep one token source and map it from Figma variables to CSS variables to Tailwind.

How do you design dark mode for a SaaS app properly?

You design dark mode properly by building it into the color system, not into the screens. Every color in the UI references a semantic token like surface, text or border, and each token has a light value and a dark value. Switching themes swaps the values; no screen is redrawn and no component is duplicated.

The common failure is the opposite: a team designs the light theme, then someone recolors a copy of every frame for dark. Within a few releases the two drift apart. This guide shows the token approach end to end, from Figma variable modes to CSS variables. It's part of our wider guide to web app UI design.

Why do semantic tokens matter for dark mode UI design?

Semantic tokens matter because dark mode changes what a color is for, not just what it looks like. A raw palette value like gray-900 means "text" in light mode and "background" in dark mode. If components reference raw values, every component needs two versions. If they reference roles, they need one.

Layer

Example name

Light value

Dark value

Primitive

gray-50, gray-900, blue-500

Fixed color

Same fixed color

Semantic: surface

surface-default

gray-50

gray-900

Semantic: raised surface

surface-raised

white

gray-800

Semantic: text

text-default

gray-900

gray-50

Semantic: muted text

text-muted

gray-600

gray-400

Semantic: border

border-subtle

gray-200

gray-700

Semantic: accent

accent-default

blue-600

blue-400

Two layers are enough for most startups: primitives (the raw palette) and semantic tokens (roles that reference primitives per theme). Component-level tokens, like button-primary-bg, are worth adding only once a design system has several product teams consuming it.

How do Figma variable modes work for light and dark mode?

Figma variable modes let one variable hold a different value per mode, so a collection of semantic color variables can have a Light mode and a Dark mode. Apply the mode to a frame or page and every layer bound to those variables switches theme at once.

  1. Create a primitives collection

    Add your raw palette as color variables with a single mode. These are never applied directly to layers.

  2. Create a semantic collection with two modes

    Add role-based variables (surface, text, border, accent, status) with Light and Dark modes, each aliasing a primitive.

  3. Bind components to semantic variables only

    Every fill, stroke and text color in your components references a semantic variable. Hide primitives from publishing so nobody uses them by mistake.

  4. Preview both modes on real screens

    Switch the mode on a full product frame (tables, charts, modals, empty states) to catch anything still hard-coded.

  5. Hand off the token table

    Export or document the semantic variables with both values so engineering mirrors the same names in code.

The payoff is that designers design once. A new screen built from bound components is automatically correct in both themes, and reviewing dark mode becomes a mode switch rather than a separate design pass.

How do you build dark mode with CSS variables and Tailwind?

Build dark mode in code by defining the same semantic tokens as CSS custom properties, with one set of values for light and another applied under a dark selector or the user's system preference. Components use the tokens, so the theme switch is a single attribute or class on the root element.

Implementation notes: from Figma variables to Tailwind v4

Define each semantic token as a CSS variable on the root element with its light value, then redefine the same variables inside a dark-theme rule. That rule can respond to the prefers-color-scheme media query, to a data-theme attribute set by a user toggle, or both, with the explicit choice winning.

In Tailwind v4, map the semantic variables into the theme so utilities like bg-surface and text-muted exist and point at the variables. Tailwind's dark variant follows the system preference by default; to support a manual toggle, redefine the dark variant to match a class or data attribute on an ancestor.

Store the user's choice (light, dark or system) and apply it before first paint, usually with a small inline script in the document head, so the page doesn't flash the wrong theme on load. Keep the token names identical to the Figma variable names so a designer and an engineer can talk about surface-raised and mean the same thing.

Offer three options in settings: Light, Dark and System. System should be the default, because it respects the choice the user already made for their device.

What colors should dark mode use?

Dark mode should use dark grays rather than pure black for backgrounds, off-white rather than pure white for text, and lighter, slightly desaturated versions of brand and status colors. These choices reduce harsh contrast and keep color from vibrating against dark surfaces.

  • Backgrounds: a dark gray base, often tinted slightly toward your brand hue, keeps the UI from feeling like a void.
  • Elevation: in dark mode, raised surfaces (cards, menus, modals) get lighter, not darker. Shadows barely show on dark backgrounds, so lightness carries depth.
  • Text: primary text slightly off pure white, secondary text a mid gray that still passes contrast.
  • Accent and status colors: saturated colors that work on white often glow on dark gray. Shift them lighter and less saturated.
  • Borders: subtle borders do more work in dark mode, because shadows can't separate surfaces.

Check every text and icon pair against contrast requirements in both themes. Muted text and disabled states are where dark themes fail most often. Our guide to accessible design basics covers contrast checks, and the accessible web app design checklist covers focus rings, which need their own dark-theme color.

What breaks most often in dark mode?

What breaks most often is everything outside the core components: images, charts, illustrations, embedded content and hard-coded colors from older code. Plan for these explicitly.

  • Logos and images with white backgrounds look like stickers. Provide transparent or dark-variant assets.
  • Charts need a dark palette of their own, with gridlines and labels from tokens. This matters on analytics screens, as covered in SaaS dashboard design best practices.
  • Skeletons and loading shimmers often use hard-coded light grays and flash bright on dark screens.
  • Emails, PDFs and exports usually stay light, and that's fine. Don't force dark mode where users expect print.
  • Third-party embeds like payment forms or chat widgets may not support dark mode at all. Frame them on a neutral surface.

Is dark mode worth it for your SaaS app?

Dark mode is worth it when users spend long sessions in your app, work in low light, or come from developer and creative tools where it's expected. For an admin panel used a few minutes a week, it's a lower priority than fixing navigation or empty states.

Frequently asked questions

Should a SaaS app default to dark mode?
Usually not. The safest default is to follow the user's system setting, which respects the choice they already made for their device. A few products default to dark because their audience and brand expect it, such as developer tools. Whatever you choose, let users override it in settings and remember their choice.
Is pure black ever a good dark mode background?
Pure black can work on OLED-focused mobile interfaces or media apps where content is the focus, but for most SaaS products a dark gray is better. It makes elevation possible, because lighter surfaces can sit on top of it, and it reduces the harsh contrast that makes white text appear to bleed into a black background.
Does dark mode need a separate brand color?
It usually needs a different value of the same brand color. Most brand colors are chosen against white and look too saturated or too dark on a dark background. Define a dark-mode accent that keeps the hue but shifts lightness and saturation, and check that it still passes contrast for text and buttons.
How do you test dark mode before launch?
Switch the theme on every key screen, including tables, charts, modals, empty states, errors and settings, rather than only the dashboard. Run contrast checks on muted text and disabled states, view the app in a dim room on a real monitor, and search the codebase for hard-coded colors that bypass your tokens.
Can a design system support more than two themes?
Yes. Once colors are semantic tokens with modes, adding a theme such as high contrast or a white-label customer brand means adding another mode with its own values. Components don't change. That's the main argument for tokens: dark mode becomes the first proof that the system can theme, not a one-off project.

· CEO & Design Director

Copywriter, designer and developer, and founder of Designify Studio. Over five years, 60+ projects across 14+ industries, for startups that have raised $300M+.

Want this done for you? Brand & design sprint, $3,000.

A brand refresh or redesign, plus design explorations for your website and product. $3,000, one-time, fixed price. Book a free 30-minute call to see if it fits.

Schedule Call