Implementación del modo oscuro en aplicaciones React usando componentes con estilo
Publicado: 2022-03-10Una de las funciones de software más solicitadas es el modo oscuro (o modo nocturno, como lo llaman otros). Vemos el modo oscuro en las aplicaciones que usamos todos los días. Desde aplicaciones móviles hasta aplicaciones web, el modo oscuro se ha vuelto vital para las empresas que quieren cuidar los ojos de sus usuarios.
El modo oscuro es una función complementaria que muestra principalmente superficies oscuras en la interfaz de usuario. La mayoría de las principales empresas (como YouTube, Twitter y Netflix) han adoptado el modo oscuro en sus aplicaciones móviles y web.
Si bien no profundizaremos en React y los componentes con estilo, un conocimiento básico de React, CSS y los componentes con estilo sería útil. Este tutorial beneficiará a aquellos que buscan mejorar sus aplicaciones web atendiendo a los amantes del modo oscuro.

Unos días antes de escribir este artículo, StackOverflow anunció el lanzamiento del modo oscuro, que brinda a los usuarios la oportunidad de alternar entre los dos modos.
El modo oscuro reduce la fatiga visual y ayuda cuando trabaja durante mucho tiempo en una computadora o teléfono móvil.
¿Qué es el modo oscuro?
El modo oscuro es el esquema de color de cualquier interfaz que muestra texto claro y elementos de la interfaz sobre un fondo oscuro, lo que hace que la pantalla sea un poco más fácil de ver en teléfonos móviles, tabletas y computadoras. El modo oscuro reduce la luz emitida por la pantalla, mientras mantiene las proporciones mínimas de contraste de color requeridas para la legibilidad.
¿Por qué debería preocuparse por el modo oscuro?
El modo oscuro mejora la ergonomía visual al reducir la fatiga visual, ajustar la pantalla a las condiciones de luz actuales y brindar facilidad de uso por la noche o en entornos oscuros.
Antes de implementar el modo oscuro en nuestra aplicación, veamos sus beneficios.
Ahorro de batería
El modo oscuro en la web y las aplicaciones móviles puede prolongar la duración de la batería de un dispositivo. Google ha confirmado que el modo oscuro en las pantallas OLED ha sido de gran ayuda para la duración de la batería.
Por ejemplo, con un brillo del 50 %, el modo oscuro de la aplicación de YouTube ahorra aproximadamente un 15 % más de energía en la pantalla que un fondo blanco plano. Con un brillo de pantalla del 100 %, la interfaz oscura ahorra la friolera de un 60 % de la energía de la pantalla.
El modo oscuro es hermoso
El modo oscuro es hermoso y puede mejorar significativamente el atractivo de la pantalla.
Si bien la mayoría de los productos buscan ese aspecto blanco suave similar, el modo oscuro ofrece algo diferente que se siente misterioso y nuevo.
También brinda grandes oportunidades para presentar contenido gráfico como tableros, imágenes y fotos de una manera fresca.

Ahora que sabe por qué debería implementar el modo oscuro en su próxima aplicación web, profundicemos en los componentes con estilo, que es el recurso definitorio de este tutorial.
El modo oscuro es el esquema de color de cualquier interfaz que muestra texto claro y elementos de la interfaz sobre un fondo oscuro, lo que hace que sea un poco más fácil de ver en teléfonos móviles, tabletas y computadoras.
“
¿Qué son los componentes con estilo?
A lo largo de este artículo, usaremos la biblioteca de componentes con estilo muy a menudo. Siempre ha habido muchas maneras de diseñar una aplicación web moderna. Existe el método tradicional de estilo a nivel de documento, que incluye la creación de un archivo index.css
y vincularlo al HTML o el estilo dentro del archivo HTML.
Mucho ha cambiado en la forma en que se diseñan las aplicaciones web recientemente, desde la introducción de CSS-in-JS.
CSS-in-JS se refiere a un patrón en el que CSS se compone usando JavaScript. Utiliza literales de plantilla etiquetados para diseñar componentes en un archivo JavaScript.
Para obtener más información sobre CSS-in-JS, consulte el artículo de Anna Monus sobre el tema.
Styled-Components es una biblioteca CSS-in-JS que le permite usar todas las características de CSS que le encantan, incluidas las consultas de medios, los pseudoselectores y el anidamiento.
¿Por qué componentes con estilo?
styled-components fue creado por las siguientes razones:
- Sin nombre de clase infierno
En lugar de rascarse la cabeza para encontrar un nombre de clase para un elemento, los componentes con estilo generan nombres de clase únicos para sus estilos. Nunca tendrá que preocuparse por las faltas de ortografía o el uso de nombres de clase que no tienen significado. - usando accesorios
Los componentes con estilo nos permiten extender las propiedades de estilo usando el parámetroprops
, comúnmente usado en React, lo que afecta dinámicamente la sensación de un componente a través del estado de la aplicación. - Soporta sintaxis Sass
Escribir la sintaxis de Sass de forma inmediata sin tener que configurar ningún preprocesador o herramientas de compilación adicionales es posible con componentes con estilo. En sus definiciones de estilo, puede usar el carácter&
para apuntar al componente actual, usar pseudoselectores y experimentar con el anidamiento. - Tematización
Los componentes con estilo tienen soporte completo de temas al exportar un componente contenedor deThemeProvider
. Este componente proporciona un tema para todos los componentes de React dentro de sí mismo a través de la API de contexto. En el árbol de representación, todos los componentes con estilo tendrán acceso al tema proporcionado, incluso cuando tengan varios niveles de profundidad. A medida que avancemos en este tutorial, profundizaremos en las funciones de tematización de los componentes con estilo.
Para conocer más ventajas de los componentes con estilo, consulte el artículo de Kris Guzman.
Implementando el modo oscuro
En este artículo, implementaremos el modo oscuro en una página web simple similar a YouTube.
Para continuar, asegúrese de clonar el repositorio original de la rama de starter
.
Configuración
Instalemos todas las dependencias en nuestro archivo package.json
. Desde la terminal, ejecute el siguiente comando:
npm install
Tras su instalación exitosa, ejecute npm start
. Así es como se ve la página web sin el modo oscuro implementado.

Para instalar styled-components
, en su terminal ejecute npm install styled-components
.
Implementación
Para implementar el modo oscuro, necesitamos crear cuatro componentes diferentes.
-
Theme
Contiene las propiedades de color de nuestros temas claros y oscuros. -
GlobalStyles
Contiene los estilos globales para todo el documento. -
Toggler
Esto contiene el elemento de botón que alterna la funcionalidad. -
useDarkMode
Este enlace personalizado maneja la lógica detrás del cambio de tema y la persistencia de nuestro tema en localStorage.
Componente de tema
En la carpeta src
, verá componentes en la carpeta de components
. Cree un archivo Themes.js
y agréguele el siguiente código.
export const lightTheme = { body: '#FFF', text: '#363537', toggleBorder: '#FFF', background: '#363537', } export const darkTheme = { body: '#363537', text: '#FAFAFA', toggleBorder: '#6B8096', background: '#999', }
Aquí, hemos definido y exportado objetos lightTheme
y darkTheme
con distintas variables de color. Siéntase libre de experimentar y personalizar las variables a su gusto.
Componente globalStyles
Permaneciendo en su carpeta de components
, cree un archivo globalStyles.js
y agregue el siguiente código:
import { createGlobalStyle} from "styled-components" export const GlobalStyles = createGlobalStyle` body { background: ${({ theme }) => theme.body}; color: ${({ theme }) => theme.text}; font-family: Tahoma, Helvetica, Arial, Roboto, sans-serif; transition: all 0.50s linear; } `
Hemos importado createGlobalStyle
de componentes con estilo. El método createGlobalStyle
reemplaza el método injectGlobal ahora en desuso de la versión 3 de componentes con estilo. Este método genera un componente React que, cuando se agrega a su árbol de componentes, inyectará estilos globales en el documento, en nuestro caso, App.js
Definimos un componente GlobalStyle
y asignamos propiedades de background
y color
a los valores del objeto del tema. Por lo tanto, cada vez que cambiamos el conmutador, los valores cambiarán según los objetos de tema oscuro o de tema claro que estemos pasando a ThemeProvider
(que se creará más adelante, a medida que avancemos).
La propiedad de transición de 0.50s
permite que este cambio se produzca de forma un poco más suave, de modo que, a medida que avanzamos y retrocedemos, podemos ver cómo se producen los cambios.
Creación de funcionalidad de cambio de tema
Para implementar la funcionalidad de cambio de tema, necesitamos agregar solo unas pocas líneas de código. En el archivo App.js
, agregue el siguiente código (tenga en cuenta que el código resaltado es lo que debe agregar):
import React, { useState, useEffect } from "react";
import {ThemeProvider} from "styled-components"; import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes"
import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]);
const [theme, setTheme] = useState('light'); const themeToggler = () => { theme === 'light' ? setTheme('dark') : setTheme('light') }
useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []); return (
<ThemeProvider theme={theme === 'light' ? lightTheme : darkTheme}> <> <GlobalStyles/>
<div className="App">
<button onClick={themeToggler}>Switch Theme</button>
{ videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div>
</> </ThemeProvider>
); }; export default App;
El código resaltado es el que se agregó recientemente a App.js
Hemos importado ThemeProvider
desde styled-components
. ThemeProvider
es un componente auxiliar en la biblioteca de componentes con estilo que proporciona compatibilidad con temas. Este componente auxiliar inyecta un tema en todos los componentes de React debajo de sí mismo a través de la API de contexto.
En el árbol de representación, todos los componentes con estilo tendrán acceso al tema proporcionado, incluso cuando tengan varios niveles de profundidad. Echa un vistazo a la sección sobre "Tematización".
A continuación, importamos el contenedor GlobalStyle
desde ./components/Globalstyle
. Por último, desde arriba, importamos los objetos lightTheme
y darkTheme
desde ./components/Themes
.

Para que podamos crear un método de alternancia, necesitamos un estado que contenga el valor de color inicial de nuestro tema. Entonces, creamos un estado de theme
y establecemos el estado inicial en light
, usando el useState
.
Ahora, para la funcionalidad de alternancia.
El método themeToggler
utiliza un operador ternario para verificar el estado del theme
y cambia entre claro o oscuro según el valor de la condición.
ThemeProvider
, un componente auxiliar de componentes con estilo, envuelve todo en la declaración de return
e inyecta cualquier componente debajo de ella. Recuerde que nuestros GlobalStyles
inyectan estilos globales en nuestros componentes; por lo tanto, se llama dentro del componente contenedor ThemeProvider
.
Por último, creamos un botón con un evento onClick
que le asigna nuestro método themeToggler
.
Veamos el resultado hasta ahora.

Nuestro archivo App.js
necesita ser refactorizado; mucho de su código no es SECO. (DRY significa "no te repitas", un principio básico del desarrollo de software destinado a reducir la repetición). Toda la lógica parece estar en App.js
; es una buena práctica separar nuestra lógica en aras de la claridad. Entonces, crearemos un componente que maneje la funcionalidad de alternancia.
Alternar componente
Todavía dentro de la carpeta de components
, cree un archivo Toggler.js
y agréguele el siguiente código:
import React from 'react' import { func, string } from 'prop-types'; import styled from "styled-components" const Button = styled.button` background: ${({ theme }) => theme.background}; border: 2px solid ${({ theme }) => theme.toggleBorder}; color: ${({ theme }) => theme.text}; border-radius: 30px; cursor: pointer; font-size:0.8rem; padding: 0.6rem; } \`; const Toggle = ({theme, toggleTheme }) => { return ( <Button onClick={toggleTheme} > Switch Theme </Button> ); }; Toggle.propTypes = { theme: string.isRequired, toggleTheme: func.isRequired, } export default Toggle;
Para mantener las cosas ordenadas, hemos diseñado nuestro botón de alternancia en el componente Toggle
, utilizando la función de styled
de componentes con estilo.
Esto es puramente para la presentación; puede diseñar el botón como mejor le parezca.
Dentro del componente Toggle
, pasamos dos accesorios:
- el
theme
proporciona el tema actual (claro u oscuro); - la función
toggleTheme
se usará para cambiar entre temas.
A continuación, devolvemos el componente Button
y asignamos una función toggleTheme
al evento onClick
.
Por último, usamos propTypes
para definir nuestros tipos, asegurándonos de que nuestro theme
sea una string
e isRequired
, mientras que nuestro toggleTheme
es func
y isRequired
.
Uso de ganchos personalizados ( useDarkMode
)
Al construir una aplicación, la escalabilidad es primordial, lo que significa que nuestra lógica de negocios debe ser reutilizable, para que podamos usarla en muchos lugares e incluso en diferentes proyectos.
Es por eso que sería genial mover nuestra funcionalidad de alternar a un componente separado. Para eso, crearíamos nuestro propio gancho personalizado.
Vamos a crear un nuevo archivo llamado useDarkMode.js
en la carpeta de components
y mover nuestra lógica a este archivo, con algunos ajustes. Agregue el siguiente código al archivo:
import { useEffect, useState } from 'react'; export const useDarkMode = () => { const [theme, setTheme] = useState('light'); const setMode = mode => { window.localStorage.setItem('theme', mode) setTheme(mode) }; const themeToggler = () => { theme === 'light' ? setMode('dark') : setMode('light') }; useEffect(() => { const localTheme = window.localStorage.getItem('theme'); localTheme && setTheme(localTheme) }, []); return [theme, themeToggler] };
Hemos añadido algunas cosas aquí.
-
setMode
UsamoslocalStorage
para persistir entre sesiones en el navegador. Entonces, si un usuario ha elegido el tema oscuro o claro, eso es lo que obtendrá en su próxima visita a la aplicación o si vuelve a cargar la página. Por lo tanto, esta función establece nuestro estado y pasa eltheme
alocalStorage
. -
themeToggler
Esta función utiliza un operador ternario para verificar el estado del tema y alterna entre oscuro o claro según la verdad de la condición. -
useEffect
Hemos implementado eluseEffect
para comprobar el montaje de componentes. Si el usuario ha seleccionado previamente un tema, lo pasaremos a nuestra funciónsetTheme
. Al final, devolveremos nuestrotheme
, que contiene eltheme
elegido y la funciónthemeToggler
para cambiar entre modos.
Creo que estará de acuerdo en que nuestro componente de modo oscuro se ve elegante.
Vayamos a App.js
para los toques finales.
import React, { useState, useEffect } from "react"; import {ThemeProvider} from "styled-components";
import {useDarkMode} from "./components/useDarkMode"
import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes" import Toggle from "./components/Toggler" import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]);
const [theme, themeToggler] = useDarkMode(); const themeMode = theme === 'light' ? lightTheme : darkTheme;
useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []); return (
<ThemeProvider theme={themeMode}>
<> <GlobalStyles/> <div className="App">
<Toggle theme={theme} toggleTheme={themeToggler} />
{ videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> </> </ThemeProvider> ); }; export default App;
El código resaltado se agregó recientemente a App.js
En primer lugar, importamos nuestro enlace personalizado, desestructuramos el theme
y los accesorios de themeToggler
, y lo configuramos con la función useDarkMode
.
Tenga en cuenta que el método useDarkMode
reemplaza nuestro estado de theme
, que inicialmente estaba en App.js
Declaramos una variable themeMode
, que representa un tema claro u oscuro según la condición del modo de theme
en ese momento.
Ahora, nuestro componente de envoltorio ThemeProvider
se asigna a nuestra variable themeMode
recientemente creada para el apoyo del theme
.
Y, por último, en lugar del botón normal, pasamos el componente Toggle
.
Recuerde que en nuestro componente Toggle
, definimos y aplicamos estilo a un botón y les pasamos el theme
y toggleTheme
como accesorios. Entonces, todo lo que tenemos que hacer es pasar estos accesorios de manera adecuada al componente Toggle
, que actuará como nuestro botón en App.js
¡Sí! Nuestro modo oscuro está configurado y persiste, sin cambiar de color cuando la página se actualiza o se visita en una nueva pestaña.
Veamos el resultado en acción:

Casi todo funciona bien, pero hay una pequeña cosa que podemos hacer para que nuestra experiencia sea espléndida. Cambie al tema oscuro y luego vuelva a cargar la página. ¿Ves que el color azul en el botón se carga antes que el gris por un breve momento? Eso sucede porque nuestro gancho useState
inicia el tema light
inicialmente. Después de eso, useEffect
se ejecuta, verifica localStorage
y solo luego establece el theme
en dark
. Pasemos a nuestro enlace personalizado useDarkMode.js
y agreguemos un pequeño código:
import { useEffect, useState } from 'react'; export const useDarkMode = () => { const [theme, setTheme] = useState('light');
const [mountedComponent, setMountedComponent] = useState(false)
const setMode = mode => { window.localStorage.setItem('theme', mode) setTheme(mode) }; const themeToggler = () => { theme === 'light' ? setMode('dark') : setMode('light') }; useEffect(() => { const localTheme = window.localStorage.getItem('theme'); localTheme ? setTheme(localTheme) : setMode('light')
setMountedComponent(true)
}, []); return [theme, themeToggler,
}, []); return [theme, themeToggler,
mountedComponent
]
};
El código resaltado es el único agregado a useDarkMode.js
. Hemos creado otro estado llamado mountedComponent
montado y establecimos el valor predeterminado en false
usando el useState
. A continuación, dentro del useEffect
, establecemos el estado del mountedComponent
en true
usando setMountedComponent
. Por último, en la matriz de return
, mountedComponent
el estado del componente montado.
Finalmente, agreguemos un poco de código en App.js
para que todo funcione.
import React, { useState, useEffect } from "react"; import {ThemeProvider} from "styled-components"; import {useDarkMode} from "./components/useDarkMode" import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes" import Toggle from "./components/Toggler" import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]);
const [theme, themeToggler, mountedComponent] = useDarkMode();
const themeMode = theme === 'light' ? lightTheme : darkTheme; useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []);
if(!mountedComponent) return <div/>
return ( <ThemeProvider theme={themeMode}> <> <GlobalStyles/> <div className="App"> <Toggle theme={theme} toggleTheme={themeToggler} /> { videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> </> </ThemeProvider> ); }; export default App;
mountedComponent
nuestro estado de componente montado como accesorio en nuestro gancho useDarkMode
y verificamos si nuestro componente se montó, porque esto es lo que sucede en el gancho useEffect
. Si aún no ha sucedido, representaremos un div
vacío.
Veamos el resultado de nuestra página web en modo oscuro.

Ahora, notará que mientras está en modo oscuro, cuando la página se recarga, el color del botón no cambia.
Conclusión
El modo oscuro se está convirtiendo cada vez más en una preferencia del usuario, e implementarlo en una aplicación web de React es mucho más fácil cuando se usa el contenedor de temas ThemeProvider
en componentes con estilo. Continúe y experimente con componentes con estilo a medida que implementa el modo oscuro; podría agregar iconos en lugar de un botón.
Comparta sus comentarios y experiencia con la función de temas en los componentes con estilo en la sección de comentarios a continuación. ¡Me encantaría ver qué se te ocurre!
El repositorio de apoyo para este artículo está disponible en GitHub. Además, échale un vistazo en CodeSandbox.
Referencias
- “Documentación”, componentes con estilo
- “Cree un modo oscuro de su aplicación usando componentes con estilo”, Tom Nolan, Medium