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.
/* 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
.darkoverride:rootdefaults. When a component usesvar(--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.
| 1 | Lightest background | Darkest background |
| 6 | Primary | Primary (brighter) |
| 10 | Darkest text | Lightest text |
Brand Color Example
Using Primary as an example, here are the actual values at key steps in both modes:
| 1 | #e8f3ff | Very light blue | #000d4d | Very deep blue |
| 6 | #165dff | Primary blue | #3c7eff | Bright blue |
| 10 | #000d4d | Very deep blue | #eaf4ff | Very 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.
| 0 | White background | #ffffff | Dark background | #141414 |
| 1 | Secondary background | #fafafa | Secondary background | #1f1f1f |
| 2 | Border | #f0f0f0 | Border | #262626 |
| 8 | Body text | #262626 | Body text | #f0f0f0 |
| 10 | Darkest text | #141414 | Lightest text | #ffffff |
Guideline: In app code, use
var(--bp-gray-0)throughvar(--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:
// 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:
// 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:
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:
/* 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
-rgbsuffix forrgba()usage. See the Color docs.
Adaptation Rules
Components follow these rules in dark mode to stay readable and comfortable:
| Text color | Use high steps (8–10) | Maintain contrast on dark backgrounds |
| Background | Use low steps (0–2) | Reduce eye strain and create immersion |
| Border | Use mid-low steps (2–3) | Stay visible without looking harsh |
| Functional color | Slightly raise primary (6) brightness | Keep brand colors readable on dark surfaces |
| Shadow | Lower opacity | Avoid 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.