Skip to content

Dark Mode ​

Birdpaper UI implements dark mode with CSS variables. Components adapt automatically—your app does not need extra dark-mode styling. Toggle a class on the root element and the full color system transitions smoothly.


How It Works ​

Dark mode is toggled by adding the .dark class to the root element. Components read colors through CSS variables, which remap between light and dark values. Component code does not change.

css
/* Light mode */
:root {
  --bp-primary-6: #165dff;
  --bp-gray-0: #ffffff;
}

/* Dark mode: under .dark, semantic variables map to the dark palette */
.dark {
  --bp-primary-6: var(--bp-primary-dark-6); /* #3c7eff */
  --bp-gray-0: var(--bp-gray-dark-0); /* #141414 */
}

Note: Variables under .dark override :root defaults. When a component uses var(--bp-primary-6), the resolved value depends on the active mode.


Scale Direction ​

In dark mode the scale direction is inverted: 1 is darkest and 10 is lightest. The same variable name therefore resolves to different hex values in each mode.

1Lightest backgroundDarkest background
6PrimaryPrimary (brighter)
10Darkest textLightest text

Brand Color Example ​

Using Primary as an example, here are the actual values at key steps in both modes:

1#e8f3ffVery light blue#000d4dVery deep blue
6#165dffPrimary blue#3c7effBright blue
10#000d4dVery deep blue#eaf4ffVery light blue

Tip: Functional colors are roughly 20% brighter in dark mode so they stay visually strong on dark backgrounds.


Neutral Mapping ​

Neutral (Gray) colors fully invert in dark mode: the lightest light-mode step maps to the darkest dark-mode step, and vice versa.

0White background#ffffffDark background#141414
1Secondary background#fafafaSecondary background#1f1f1f
2Border#f0f0f0Border#262626
8Body text#262626Body text#f0f0f0
10Darkest text#141414Lightest text#ffffff

Guideline: In app code, use var(--bp-gray-0) through var(--bp-gray-10) and ignore the active mode—the values switch automatically. Never hard-code hex colors in business styles.


Usage ​

Toggle Dark Mode ​

Add or remove the .dark class on the root element:

js
// Enable dark mode
document.documentElement.classList.add("dark");

// Disable dark mode (back to light)
document.documentElement.classList.remove("dark");

Persist Preference ​

Store the user’s preference in localStorage and restore it on load:

js
// Save on toggle
const toggleDark = () => {
  const isDark = document.documentElement.classList.toggle("dark");
  localStorage.setItem("theme", isDark ? "dark" : "light");
};

// Restore on load
const saved = localStorage.getItem("theme");
if (saved === "dark") {
  document.documentElement.classList.add("dark");
}

Follow System Preference ​

You can also mirror the OS setting:

js
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");

// Match system on init
if (prefersDark.matches) {
  document.documentElement.classList.add("dark");
}

// Listen for OS changes
prefersDark.addEventListener("change", (e) => {
  document.documentElement.classList.toggle("dark", e.matches);
});

Use in CSS ​

Components already adapt via CSS variables. For custom styles in your app, reference the same variables:

css
/* Variables switch automatically with the active mode */
.my-element {
  color: var(--bp-gray-10);
  background: var(--bp-gray-0);
  border: 1px solid var(--bp-gray-2);
}

/* Use RGB variants when you need opacity */
.my-overlay {
  background: rgba(var(--bp-gray-0-rgb), 0.8);
}

Tip: Every color variable also has an -rgb suffix for rgba() usage. See the Color docs.


Adaptation Rules ​

Components follow these rules in dark mode to stay readable and comfortable:

Text colorUse high steps (8–10)Maintain contrast on dark backgrounds
BackgroundUse low steps (0–2)Reduce eye strain and create immersion
BorderUse mid-low steps (2–3)Stay visible without looking harsh
Functional colorSlightly raise primary (6) brightnessKeep brand colors readable on dark surfaces
ShadowLower opacityAvoid harsh edges on dark backgrounds

Guideline: Custom components that follow these rules stay visually aligned with Birdpaper UI in dark mode. The core principle: always use semantic CSS variables—never hard-code color values.