Un modo oscuro bien hecho respeta la preferencia del sistema operativo y, además, deja que la persona lo cambie en tu página. Con variables de CSS se escribe una sola vez; con :has(), el interruptor ni siquiera necesita JavaScript.
También te puede interesar
El código
<label class="interruptor"> <input type="checkbox" id="oscuro"> Modo oscuro </label> <main> <h1>Mi página</h1> <p>El texto y el fondo cambian juntos, y los controles del formulario también.</p> <input placeholder="Un campo de texto"> </main>
:root {
color-scheme: light;
--fondo: #ffffff;
--texto: #33475b;
--titulo: #0f1f33;
}
/* 1) La preferencia del sistema */
@media (prefers-color-scheme: dark) {
:root {
color-scheme: dark;
--fondo: #0f1f33;
--texto: #c9d6e4;
--titulo: #ffffff;
}
}
/* 2) El interruptor de la página manda sobre el sistema */
:root:has(#oscuro:checked) {
color-scheme: dark;
--fondo: #0f1f33;
--texto: #c9d6e4;
--titulo: #ffffff;
}
body {
background: var(--fondo);
color: var(--texto);
font-family: system-ui, sans-serif;
}
h1 { color: var(--titulo); }Marca la casilla: el fondo pasa de rgb(255, 255, 255) a rgb(15, 31, 51), comprobado en los tres navegadores. Si tu sistema ya está en modo oscuro, la página empieza oscura sin tocar nada.
Las tres piezas
- Variables. Todos los colores salen de unas pocas propiedades personalizadas (
--fondo,--texto…). Cambiar de tema es cambiar esas variables; el resto de la hoja no se toca. prefers-color-scheme: darkse cumple cuando el sistema operativo está en modo oscuro. Es lo mínimo: con esto la página ya respeta la preferencia de cada quien.color-schemeavisa al navegador de que la página está en oscuro, y él adapta lo que pinta por su cuenta: los campos de formulario, las barras de desplazamiento, el fondo antes de que cargue tu CSS (quedadarkal activar el interruptor). Sin esta línea, verás campos blancos sobre una página oscura.
El interruptor, sin JavaScript
:root:has(#oscuro:checked) se lee «la raíz del documento, si contiene una casilla #oscuro marcada». Como va después de la media query, manda sobre la preferencia del sistema. :has() funciona en Chrome 105, Safari 15.4 y Firefox 121 en adelante.
Lo que este interruptor no hace es recordar la elección: al recargar, la casilla vuelve a estar desmarcada. Para guardarla hacen falta tres líneas de JavaScript con localStorage, que pongan el atributo checked al cargar.
Cada fragmento de HTML de esta página pasó por el Nu Html Checker 26.11.4 (el validador del W3C) y cada resultado se pintó y se midió en Chromium, Firefox y WebKit antes de publicarla. Prueba tus cambios en el editor de HTML y CSS o aprende desde cero con el curso.