
Tema claro/oscuro interactivo con Tailwind CSS + daisyui + Astro View Transicions
Introducción
En este artículo, explicaré cómo integré un botón con los íconos de sol y luna en mi blog, permitiendo alternar entre los modos claro y oscuro. Utilicé Astro en conjunto con Tailwind CSS y la librería DaisyUI, que es una biblioteca de componentes diseñada específicamente para Tailwind CSS y que facilita la gestión de temas visuales.
Aunque existe suficiente documentación sobre cómo implementar esta funcionalidad con Tailwind CSS, gran parte de ella no incluye DaisyUI, la cual maneja los temas de manera distinta, lo que introduce algunas variaciones en el código.
Requisitos
Para empezar, es necesario tener Tailwind CSS instalado. Para ello, puedes ejecutar el siguiente comando:
astro add tailwind
Si no tienes Astro instalado a nivel global, puedes ejecutar este comando con npx
:
npx astro add tailwind
Luego, instala DaisyUI con el siguiente comando:
npm i -D daisyui@latest
A continuación, debes agregar DaisyUI a tu archivo tailwind.config.js
:
/** @type {import('tailwindcss').Config} */
module.exports = {
plugins: [
require('daisyui'),
],
daisyui: {
themes: ["light", "dark"], // false: solo light + dark | true: todos los temas | array: temas específicos como ["light", "dark", "cupcake"]
}
}
Si no especificas el array
themes
, DaisyUI utilizará por defecto los temaslight
ydark
, que son los dos que vamos a implementar. Si decides utilizar otros temas, tendrás que ajustar el código para reflejar estos cambios.
Para simplificar la implementación y hacerlo más claro, he creado un componente único en Astro que incluye toda la funcionalidad del botón para cambiar entre temas.
Archivo src/components/ThemeToggle.astro
<!-- src/components/ThemeToggle.astro -->
<button id="theme-toggle-btn" class="ml-2 max-sm:ml-4 icon-button" aria-label="Toggle theme">Light/Dark</button>
<script is:inline>
const defaultTheme = 'light'; // Tema predeterminado
// Íconos de los temas
const lightIcon = '<svg ...></svg>'; // SVG del tema claro
const darkIcon = '<svg ...></svg>'; // SVG del tema oscuro
// Cambia el tema y actualiza el ícono
const toggleTheme = (themeToggleBtn) => {
const currentTheme = document.documentElement.getAttribute('data-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
themeToggleBtn.innerHTML = newTheme === 'light' ? darkIcon : lightIcon;
};
// Ajusta el ícono basado en el tema actual
const adjustIcon = (themeToggleBtn, theme) => {
themeToggleBtn.innerHTML = theme === 'light' ? darkIcon : lightIcon;
};
const init = () => {
const themeToggleBtn = document.getElementById('theme-toggle-btn');
if (!themeToggleBtn) return; // Verifica si el botón existe
// Añade el evento de clic para cambiar el tema
themeToggleBtn.addEventListener("click", () => toggleTheme(themeToggleBtn));
// Recupera el tema guardado o establece el tema por defecto
const savedTheme = localStorage.getItem('theme') || defaultTheme;
document.documentElement.setAttribute('data-theme', savedTheme);
adjustIcon(themeToggleBtn, savedTheme);
};
// Inicializa cuando la página carga
init();
// Re-inicializa después del intercambio de páginas (si es necesario)
document.addEventListener("astro:after-swap", init);
</script>
<style>
#theme-toggle-btn {
right: 15px;
top: 10px;
position: absolute;
}
</style>
Explicación del Código
1. Botón HTML:
<button id="theme-toggle-btn" class="ml-2 max-sm:ml-4 icon-button" aria-label="Toggle theme">Light/Dark</button>
Este botón permite alternar entre los temas claro y oscuro. Tiene un id
para poder ser referenciado desde el script y su clase se encarga de aplicar el estilo visual.
2. Definición de Íconos:
const lightIcon = '<svg>...</svg>';
const darkIcon = '<svg>...</svg>';
Estos íconos representan el sol y la luna, que se mostrarán dependiendo del tema actual. No he incluido el código completo de los íconos SVG, pero puedes encontrarlos fácilmente en línea.
3. Función toggleTheme
:
const toggleTheme = (themeToggleBtn) => {
const currentTheme = document.documentElement.getAttribute('data-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
themeToggleBtn.innerHTML = newTheme === 'light' ? darkIcon : lightIcon;
};
Esta función alterna entre los temas claro y oscuro, actualiza el atributo data-theme
en el documento y guarda la preferencia en localStorage
para que persista entre cargas de página.
4. Función adjustIcon
:
const adjustIcon = (themeToggleBtn, theme) => {
themeToggleBtn.innerHTML = theme === 'light' ? darkIcon : lightIcon;
};
Ajusta el ícono del botón según el tema actual, permitiendo que el botón refleje correctamente si el tema es claro o oscuro.
5. Función init
:
const init = () => {
const themeToggleBtn = document.getElementById('theme-toggle-btn');
if (!themeToggleBtn) return;
themeToggleBtn.addEventListener("click", () => toggleTheme(themeToggleBtn));
const savedTheme = localStorage.getItem('theme') || defaultTheme;
document.documentElement.setAttribute('data-theme', savedTheme);
adjustIcon(themeToggleBtn, savedTheme);
};
Esta función inicializa el botón y ajusta el tema basado en el valor guardado en localStorage
o en el tema predeterminado.
6. Evento astro:after-swap
:
document.addEventListener("astro:after-swap", init);
Este evento asegura que el botón se reinicialice cuando cambias de página en una aplicación Astro con navegación SPA.
7. Estilos del Botón:
#theme-toggle-btn {
right: 15px;
top: 10px;
position: absolute;
}
El botón se posiciona de manera absoluta en la esquina superior derecha de la página, pero puedes ajustarlo a tu preferencia.
Resumen del Funcionamiento:
- Al cargar la página, el script revisa si hay un tema almacenado en el navegador. Si existe, lo aplica; si no, usa el tema predeterminado.
- El botón permite alternar entre los temas claro y oscuro, cambiando el ícono que refleja el tema activo.
- El tema seleccionado se guarda en
localStorage
para que se conserve en futuras visitas. - En aplicaciones Astro con navegación mejorada (SPA), el script se reinicializa automáticamente tras cada cambio de página.