Crear un botón de modo oscuro en React con Tailwind
Durante el rediseño del blog me sorprendió lo fácil que era agregar un modo oscuro en Tailwind, así que pensé en por qué no agregar un pequeño botón que pudiera cambiarlo a voluntad (nada del otro mundo), y ya que iba a hacer eso, también pensé en relatar el proceso 🙂.
En este tutorial aprenderemos a crear un sencillo botón de y hacia modo oscuro dentro de nuestra aplicación en React con Tailwind CSS.
Criterios de aceptación
Como siempre, antes de comenzar, definamos los criterios de aceptación para esta funcionalidad:
- Crear una aplicación con modo oscuro
- Agregar un botón que cambie el modo de la aplicación manualmente
- El modo debe cambiar automáticamente si el usuario tiene configurado el modo oscuro en su sistema operativo
Creo que con eso es suficiente, ahora sí, ¡manos a la obra!
Pre-requsitos
Mentí 🙈, antes de comenzar… Este tutorial asume que se poseen conocimientos de React intermedios (o básicos si así los consideras). Además, necesitarás una aplicación en React.
Si aún no la tienes, te sugiero utilizar Vite. Puedes encontrar más información en la documentación oficial.
Es bastante sencillo y el asistente te ayudará a travéz del proceso, ahora sí, ¡manos a la obra! (de verdad esta vez).
Paso 1: Crear el modo oscuro
Antes que nada, necesitamos que nuestra aplicación tenga un modo oscuro al que cambiar, para efectos prácticos de este tutorial, crearemos un sitio muy básico utilizando Tailwind. Por ejemplo, esta sencilla aplicación:
1<main>2 <div className='flex flex-col items-center px-4'>3 <h1 className='mt-8 text-4xl font-bold'>¡Hola, mundo!</h1>4 <p className='mt-4 text-lg'>¡Bienvenido a mi sitio web!</p>5 </div>6</main>7
Paso 1.1: Crear El "modo light"
La magia de Tailwind (o desgracia, dependiendo de a quién le preguntes) es que
puedes agregar todos los estilos que necesites por medio de utility classes,1
así que para darle forma a nuestro sitio, simplemente agregamos los colores que
necesitamos en forma de clases bg-
y text-
:
1<main>2 <div className='flex flex-col items-center bg-yellow-100 p-4 text-slate-900'>3 <h1 className='mt-8 text-4xl font-bold text-red-700'>¡Hola, mundo!</h1>4 <p className='mt-4 text-lg'>¡Bienvenido a mi sitio web!</p>5 </div>6</main>7
Lo que nos daría algo más o menos así:
¡Hola, mundo!
¡Bienvenido a mi sitio web!
Puedes encontrar más información sobre los colores de Tailwind en la documentación oficial. O puedes extender la paleta de color con tu propia configuración.
Este tipo de patrón es… polémico en la comunidad de desarrollo web, pero por motivos prácticos y para efectos de este tutorial, nos sirve muy bien. La ventaja de este enfoque es que podemos cambiar los colores de nuestra aplicación sin tener que modificar el código fuente, simplemente cambiando las clases CSS. Los que nos da una mayor velocidad de desarrollo, pero puede volverse complicado de mantener en aplicaciones más grandes.
Paso 1.2: Crear el "modo oscuro"
Una vez que tenemos una idea de como queremos que se vea nuestro sitio durante
el día, podemos comenzar a jugar con el modo oscuro, para esto (y porque usamos Tailwind),
simplemente agregamos las clases de color que necesitamos para el modo oscuro
con el prefijo dark:
.
1<main>2 <div className='flex flex-col items-center bg-yellow-100 p-4 text-slate-900 dark:bg-orange-950 dark:text-yellow-50'>3 <h1 className='mt-8 text-4xl font-bold text-red-700 dark:text-red-200'>4 ¡Hola, mundo!5 </h1>6 <p className='mt-4 text-lg'>¡Bienvenido a mi sitio web!</p>7 </div>8</main>9
¿Fácil, no? ¡Pues eso es todo! Lo que hace Tailwind detrás de bambalinas es buscar
la preferencia del usuario, si tiene configurado el modo oscuro en su sistema operativo,
automáticamente aplicará las clases dark:
a los elementos que tengan esa clase.
Así, lo que normalmente haríamos así:
1@media (prefers-color-scheme: dark) {2 :root {3 --color--foreground: #431407; /* bg-orange-950 */4 --color--background: #fefce8; /* text-yellow-50 */5 }6}7
Ahora simplemente lo hacemos así:
1<div className='dark:bg-orange-950 dark:text-yellow-50'>¡Hola, mundo!</div>2
Además de el prefijo dark
, Tailwind también ofrece los prefijos hover:
,
::after
, lg:
, entre otros.
Y una vez que las preferencias del usuario cambien, Tailwind se encargará de cambiar los colores automáticamente:
¡Hola, mundo!
¡Bienvenido a mi sitio web!
Paso 2: Agregar un botón para de cambio de modo
Hasta ahora tenemos un sitio web que cambia de luz a oscuridad dependendiendo de las preferencias del sistema operativo del usuario, pero, ¿Qué pasa si el usuario quiere cambiarlo manualmente? Para eso necesitamos un botón.
Paso 2.1 Creando el botón
Como seguimos utilizando Tailwind, podemos agregar un botón rápidamente con
las clases bg-
, text-
, rounded
, p-
.
1<button2 aria-label='Cambiar a modo oscuro'3 className='w-min rounded-full bg-yellow-800 p-2 text-yellow-50 dark:bg-yellow-100 dark:text-slate-900'4>5 <SunIcon className='h-6 w-6' />6</button>7
Y terminaremos con algo así:
Asegúrate de que el botón tenga un aria-label
para que sea accesible.
¿Recuerdas lo que dije sobre las utility classes? Bueno, aquí es donde comienza
a ponerse complicado, si te fijas el atributo className
del botón es un poco
largo, y si necesitamos cambiar el color de fondo, tendríamos que cambiarlo en
dos lugares, lo que puede ser un poco tedioso. Para evitar esto, puedes crear
tus propios componentes reutilizables.
Y con las clases que acabamos de agregar, el botón cambiará así:
Paso 2.2: Juntando los estilos
Ahora que tenemos nuestro componente y nuestro botón, necesitamos juntarlos, para esto, no explicaré todas las clases, pero básicamente necesitamos un contenedor extra y agregar el botón a nuestro componente.
1<main>2 <div className='flex flex-col items-center bg-yellow-100 p-8 text-slate-900'>3 <div className='flex w-full justify-end'>4 <button5 aria-label='Cambiar a modo oscuro'6 className='w-min rounded-full bg-yellow-800 p-2 text-yellow-50 dark:bg-yellow-100 dark:text-slate-900'7 >8 <SunIcon className='h-6 w-6' />9 </button>10 </div>1112 <h1 className='text-4xl font-bold text-red-700'>¡Hola, mundo!</h1>13 <p className='mt-4 text-lg'>¡Bienvenido a mi sitio web!</p>14 </div>15</main>16
Y tendremos nuestro resultado:
¡Hola, mundo!
¡Bienvenido a mi sitio web!
Paso 3: Cambiando el modo
Hasta el momento nuestro componente cambiará de color automáticamente dependiendo
de las preferencias del sistema operativo del usuario, pero ahora necesitamos que
nuestro botón active el cambio manualmente. En React, podemos utilizar useState
para manejar el estado de nuestro componente.
1import { useState } from 'react';23export function Component() {4 const [isDarkModeActivated, setIsDarkModeActivated] = useState(false);56 const toggleDarkMode = () => {7 setIsDarkModeActivated(!isDarkModeActivated);8 };910 return (11 <main>12 <div className='flex flex-col items-center bg-yellow-100 p-4 text-slate-900 dark:bg-orange-950 dark:text-yellow-50'>13 <div className='flex w-full justify-end'>14 <button15 aria-label='Cambiar a modo oscuro'16 className='w-min rounded-full bg-yellow-800 p-2 text-yellow-50 dark:bg-yellow-100 dark:text-slate-900'17 onClick={toggleDarkMode}18 >19 <SunIcon className='h-6 w-6' />20 </button>21 </div>2223 <h1 className='mt-8 text-4xl font-bold text-red-700 dark:text-red-200'>24 ¡Hola, mundo!25 </h1>26 <p className='mt-4 text-lg'>¡Bienvenido a mi sitio web!</p>27 </div>28 </main>29 );30}31
Con eso, al dar click a nuestro botón, el estado de isDarkModeActivated
cambiará
(será revertido entre true
y false
).
Tailwind ofrece otra forma para poder activar el modo oscuro, para que no solo
dependa de las preferencias del sistema operativo, si no que también al agregar
una clase dark
al html
, sin embargo esta debe ser activada en el archivo
de configuración de Tailwind: tailwind.config.ts
.
1import type { Config } from 'tailwindcss';23const config: Config = {4 darkMode: 'selector',5};67export default config;8
La documentación oficial indica que la case dark
debe ser agregada al <html>
para que funcione correctamente. Sin embargo me di cuenta de que solo necesita estar
en el elemento padre del componente que queremos cambiar. Normalmente queremos que
todo el sitio cambie, por lo que lo agregamos al <html>
. Aquí simplemente lo
agregamos al contenedor principal.
Y si ahora agregamos la clase dark
al contenedor principal, nuestro componente mágicamente
cambiará de color:
¡Hola, mundo!
¡Bienvenido a mi sitio web!
Paso 3.1: Enchufando el botón
Cuando damos click al botón, el estado de nuestra aplicación cambia, pero ¿Cómo
hacemos que cambie el color de nuestro componente? Para esto, simplemente necesitamos
agregar la clase dark
al contenedor principal si isDarkModeActivated
es true
.
1import { useState } from 'react';23export function Component() {4 const [isDarkModeActivated, setIsDarkModeActivated] = useState(false);56 const toggleDarkMode = () => {7 setIsDarkModeActivated(!isDarkModeActivated);8 };910 return (11 <main className={isDarkModeActivated ? 'dark' : ''}>12 <div className='flex flex-col items-center bg-yellow-100 p-4 text-slate-900 dark:bg-orange-950 dark:text-yellow-50'>13 <div className='flex w-full justify-end'>14 <button15 aria-label='Cambiar a modo oscuro'16 className='w-min rounded-full bg-yellow-800 p-2 text-yellow-50 dark:bg-yellow-100 dark:text-slate-900'17 onClick={toggleDarkMode}18 >19 <SunIcon className='h-6 w-6' />20 </button>21 </div>2223 <h1 className='mt-8 text-4xl font-bold text-red-700 dark:text-red-200'>24 ¡Hola, mundo!25 </h1>26 <p className='mt-4 text-lg'>¡Bienvenido a mi sitio web!</p>27 </div>28 </main>29 );30}31
Y con eso, al dar click al botón, el color de nuestro componente cambiará, ¡Pruébalo!
¡Hola, mundo!
¡Bienvenido a mi sitio web!
Paso 3.2 Mantener preferencias del sistema operativo
Hay un pequeño detalle que viene con esta implementación, y es que cuando seleccionamos
el modo oscuro selector
como lo hicimos más arriba, Tailwind no se encargará de
cambiar el color de nuestro componente si el usuario cambia las preferencias del sistema,
esto afortunadamente es fácil de solucionar, simplemente agregamos el media query dentro
del mismo archivo de configuración de Tailwind. Así:
1import type { Config } from "tailwindcss";23const config: Config = {4darkMode: [ 'variant, [5'&.is(.dark *)',6'media (prefers-color-scheme: dark) { & * }',7],8};910export default config;11
La forma en que esto funciona, es gracias a la magia de &
, que hace referencia al
selector actual, en este caso html
, y *
que hace referencia a todos los elementos
dentro de html
. Así, lo que estamos diciendo es:
- Si el selector actual (
html
) tiene la clasedark
, entonces aplica los estilos a todos los hijos. - Si el sistema operativo tiene configurado el modo oscuro, entonces aplica los estilos a todos los hijos de
html
.
Y con eso, Tailwind se encargará de cambiar el color de nuestro componente si el usuario cambia las preferencias de su sistema operativo.
Siguentes pasos
Y con eso, hemos terminado, para recapitular, veamos los requerimientos que teníamos:
- Crear una aplicación con modo oscuro
- Agregar un botón que cambie el modo de la aplicación manualmente
- El modo debe cambiar automáticamente si el usuario tiene configurado el modo oscuro en su sistema operativo
Hemos completado todos los requerimientos, sin embargo en mi opinión aún hay mucho que se puede hacer para mejorar nuestro botón, que tal vez podrías intentar (o lo dejaré para otro tutorial), por ejemplo:
- Agregar una animación al botón
- Cambiar el ícono del botón dependiendo del estado del modo oscuro
- Agregar un tooltip al botón
- Guardar la preferencia manual del usuario en el
localStorage
Y muchas otras cosas más, pero por ahora, creo que es suficiente. Si tienes alguna duda o sugerencia, no dudes en contactarme a travéz de mis redes sociales o el formulario de contacto.
¡Gracias por leer!