Se va a crear un sistema de temas dinámico profesional con CSS variables para crear un tema oscuro, normal o de una marca.

- Definir los Design Tokens base
Primero definimos la estructura de variables en :root.
No ponemos colores directos en componentes. Solo usamos variables semánticas.
:root {
/* Colores semánticos */
--color-bg: #ffffff;
--color-surface: #f5f5f5;
--color-primary: #1976d2;
--color-text: #222222;
--color-text-muted: #666666;
/* Bordes */
--radius-md: 8px;
/* Sombras */
--shadow-md: 0 4px 10px rgba(0, 0, 0, 0.1);
}
Clave profesional:
No llames a una variable --blue. Llámala --color-primary.
Así puedes cambiar el color sin cambiar el significado.
- Crear el tema dark
Ahora redefinimos solo las variables necesarias bajo una clase.
.theme-dark {
--color-bg: #121212;
--color-surface: #1e1e1e;
--color-primary: #90caf9;
--color-text: #ffffff;
--color-text-muted: #bbbbbb;
--shadow-md: 0 4px 10px rgba(0, 0, 0, 0.4);
}
Fíjate: no duplicamos estilos, solo cambiamos variables.
- Crear un tema de marca personalizado
Imagina que quieres un modo “startup verde” o una marca diferente.
.theme-brand-green {
--color-primary: #2e7d32;
}
.theme-brand-purple {
--color-primary: #6a1b9a;
}
Puedes combinar clases:
<body class="theme-dark theme-brand-purple">
Esto permite:
Modo oscuro + marca morada
Modo claro + marca verde
Combinaciones sin duplicar CSS
- Usar las variables en los componentes
Ejemplo real de estilos:
body {
background-color: var(--color-bg);
color: var(--color-text);
transition: background-color 0.3s ease, color 0.3s ease;
}
.card {
background-color: var(--color-surface);
border-radius: var(--radius-md);
box-shadow: var(--shadow-md);
padding: 16px;
}
.button {
background-color: var(--color-primary);
color: white;
border: none;
border-radius: var(--radius-md);
padding: 10px 16px;
cursor: pointer;
}
Observa que ningún componente sabe si está en dark o light.
Solo usa variables.
Eso es arquitectura limpia.
- Cambiar tema dinámicamente con JavaScript
Ahora lo interesante.
function setTheme(themeName) {
document.body.className = themeName;
}
Botones de ejemplo:
<button onclick="setTheme('')">Light</button>
<button onclick="setTheme('theme-dark')">Dark</button>
<button onclick="setTheme('theme-dark theme-brand-purple')">
Dark Purple
</button>
- Persistir la preferencia del usuario
Aquí es donde ya suena a producto real.
function setTheme(themeName) {
document.body.className = themeName;
localStorage.setItem("theme", themeName);
}
function loadTheme() {
const savedTheme = localStorage.getItem("theme");
if (savedTheme) {
document.body.className = savedTheme;
}
}
loadTheme();
Ahora el usuario vuelve y mantiene su tema.
- Detectar automáticamente el modo del sistema
Si quieres hacerlo nivel “empresa seria”:
function detectSystemTheme() {
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
return prefersDark ? "theme-dark" : "";
}
function loadTheme() {
const savedTheme = localStorage.getItem("theme");
if (savedTheme) {
document.body.className = savedTheme;
} else {
document.body.className = detectSystemTheme();
}
}
loadTheme();
Esto hace que el primer render respete el sistema del usuario.
- Arquitectura recomendada en proyecto real
Estructura limpia:
styles
tokens.css
themes.css
components.css
tokens.css → variables base
themes.css → clases theme-dark, theme-brand
components.css → estilos usando var()
Separación clara. Escalable.
- Nivel avanzado: evitar “flash” al cargar
Problema clásico:
La página carga en light y luego cambia a dark.
Solución:
Inyectar el script antes del render:
<script>
const savedTheme = localStorage.getItem("theme");
if (savedTheme) {
document.documentElement.className = savedTheme;
}
</script>

Ingeniero en Informática, Investigador, me encanta crear cosas o arreglarlas y darles una nueva vida. Escritor y poeta. Más de 20 APPs publicadas y un libro en Amazon.