upd: я сделал видео гайд по созданию темной темы и исправлению проблем, которые могут возникнуть

На входе

if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
    // dark mode
}

В реальном времени

window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
    const newColorScheme = event.matches ? "dark" : "light";
});

Я использую этот метод в своей подборке ссылок. На момент написания это выглядит так:

В CSS я просто добавил класс dark, который динамически миксуется к классу page, переписывая цвета специфичностью (два класса). Не по БЭМу, но как часто советуют сначала делал, чтобы работало) Потом может перепишу под БЭМ.

.dark.page {background-color:#002B36;} 
.dark header {color:#c3d5d9;} 
и так далее

JS:

const page = document.querySelector('.page');
// Theme switch buttons
const lightThemeLink = document.querySelector('.themes__link_light');
const darkThemeLink = document.querySelector('.themes__link_dark');

// Check if user set dark theme before
if (localStorage.getItem('themeIs') == 'Dark') {
	page.classList.add('dark');
}

// Check if user prefers dark theme
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
	page.classList.add('dark');
}

// Check theme preference switch real time
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
	const newColorScheme = event.matches ? 'dark' : 'light';
	if (newColorScheme == 'dark') {
		page.classList.add('dark');
	}
	if (newColorScheme == 'light') {
		page.classList.remove('dark');
	} 
});

// Turn dark theme on
darkThemeLink.addEventListener('click', setDark);
function setDark(el) {
	el.preventDefault();
	page.classList.add('dark');
	localStorage.setItem('themeIs', 'Dark');
}

// Turn light theme on
lightThemeLink.addEventListener('click', setLight);
function setLight(el) {
	el.preventDefault();
	page.classList.remove('dark');
		localStorage.setItem('themeIs', 'Light');
}

Это, кажется, покрывает все кейсы.

В CSS есть медиа-запрос prefers-color-scheme, но у него гораздо уже поддержка и без JS все равно не обойтись (интерактивное переключение).

Чтобы избежать мигания белого фона во время загрузки используйте этот метод.