Skip to main content

Crear un botón de modo oscuro en React con Tailwind

Publicado hace
Actualizado hace
8 minutos de lectura

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!

Consejo

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
Nota

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<button
2 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í:

Advertencia

Asegúrate de que el botón tenga un aria-label para que sea accesible.

Precaución

¿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 <button
5 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>
11
12 <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';
2
3export function Component() {
4 const [isDarkModeActivated, setIsDarkModeActivated] = useState(false);
5
6 const toggleDarkMode = () => {
7 setIsDarkModeActivated(!isDarkModeActivated);
8 };
9
10 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 <button
15 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>
22
23 <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';
2
3const config: Config = {
4 darkMode: 'selector',
5};
6
7export default config;
8
Nota

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';
2
3export function Component() {
4 const [isDarkModeActivated, setIsDarkModeActivated] = useState(false);
5
6 const toggleDarkMode = () => {
7 setIsDarkModeActivated(!isDarkModeActivated);
8 };
9
10 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 <button
15 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>
22
23 <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";
2
3const config: Config = {
4darkMode: [ 'variant, [
5'&.is(.dark *)',
6'media (prefers-color-scheme: dark) { & * }',
7],
8};
9
10export default config;
11
Nota

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 clase dark, 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!