Implementando o modo escuro em aplicativos React usando componentes com estilo
Publicados: 2022-03-10Um dos recursos de software mais solicitados é o modo escuro (ou modo noturno, como outros o chamam). Vemos o modo escuro nos aplicativos que usamos todos os dias. De aplicativos móveis a aplicativos da web, o modo escuro se tornou vital para empresas que desejam cuidar dos olhos de seus usuários.
O modo escuro é um recurso complementar que exibe principalmente superfícies escuras na interface do usuário. A maioria das grandes empresas (como YouTube, Twitter e Netflix) adotou o modo escuro em seus aplicativos móveis e da web.
Embora não nos aprofundemos em React e componentes com estilo, um conhecimento básico de React, CSS e componentes com estilo seria útil. Este tutorial beneficiará aqueles que desejam aprimorar seus aplicativos da Web, atendendo àqueles que amam o modo escuro.

Alguns dias antes da redação deste artigo, o StackOverflow anunciou o lançamento do modo escuro, dando aos usuários a chance de alternar entre os dois modos.
O modo escuro reduz o cansaço visual e ajuda quando você está trabalhando por muito tempo em um computador ou telefone celular.
O que é o modo escuro?
O modo escuro é o esquema de cores de qualquer interface que exiba texto claro e elementos de interface em um fundo escuro, o que torna a tela um pouco mais fácil de ver em telefones celulares, tablets e computadores. O modo escuro reduz a luz emitida pela tela, mantendo as taxas mínimas de contraste de cor necessárias para a legibilidade.
Por que você deve se preocupar com o modo escuro?
O modo escuro melhora a ergonomia visual reduzindo o cansaço visual, ajustando a tela às condições de luz atuais e proporcionando facilidade de uso à noite ou em ambientes escuros.
Antes de implementar o modo escuro em nosso aplicativo, vejamos seus benefícios.
Economia de bateria
O modo escuro em aplicativos da Web e móveis pode prolongar a vida útil da bateria de um dispositivo. O Google confirmou que o modo escuro nas telas OLED tem sido uma grande ajuda para a duração da bateria.
Por exemplo, com 50% de brilho, o modo escuro no aplicativo do YouTube economiza cerca de 15% mais energia de tela do que um fundo branco plano. Com 100% de brilho da tela, a interface escura economiza 60% da energia da tela.
O modo escuro é lindo
O modo escuro é bonito e pode melhorar significativamente o apelo da tela.
Embora a maioria dos produtos tenha um visual branco suave semelhante, o modo escuro oferece algo diferente que parece misterioso e novo.
Ele também oferece ótimas oportunidades para apresentar conteúdo gráfico, como painéis, imagens e fotos de uma maneira nova.

Agora que você sabe por que deve implementar o modo escuro em seu próximo aplicativo da Web, vamos nos aprofundar nos componentes com estilo, que é o recurso definidor deste tutorial.
O modo escuro é o esquema de cores de qualquer interface que exibe texto claro e elementos de interface em um fundo escuro, o que facilita um pouco a visualização em telefones celulares, tablets e computadores.
“
O que são componentes com estilo?
Ao longo deste artigo, usaremos a biblioteca de componentes com estilo com muita frequência. Sempre houve muitas maneiras de estilizar um aplicativo da web moderno. Existe o método tradicional de estilização no nível do documento, que inclui criar um arquivo index.css
e vinculá-lo ao HTML ou estilizar dentro do arquivo HTML.
Muito mudou na forma como os aplicativos da web são estilizados recentemente, desde a introdução do CSS-in-JS.
CSS-in-JS refere-se a um padrão no qual CSS é composto usando JavaScript. Ele utiliza literais de modelo marcados para estilizar componentes em um arquivo JavaScript.
Para saber mais sobre CSS-in-JS, confira o artigo de Anna Monus sobre o assunto.
styled-components é uma biblioteca CSS-in-JS que permite usar todos os recursos de CSS que você adora, incluindo consultas de mídia, pseudosseletores e aninhamento.
Por que componentes com estilo?
styled-components foi criado pelos seguintes motivos:
- Sem nome de classe inferno
Em vez de você coçar a cabeça para encontrar um nome de classe para um elemento, styled-components gera nomes de classe exclusivos para seus estilos. Você nunca terá que se preocupar com erros ortográficos ou usar nomes de classe sem significado. - Usando adereços
styled-components nos permitem estender propriedades de estilo usando o parâmetroprops
, comumente usado em React — assim, afetando dinamicamente a sensação de um componente através do estado do aplicativo. - Suporta sintaxe Sass
Escrever a sintaxe Sass fora da caixa sem ter que configurar nenhum pré-processador ou ferramentas de compilação extras é possível com componentes com estilo. Em suas definições de estilo, você pode usar o caractere&
para direcionar o componente atual, usar pseudosseletores e experimentar o aninhamento. - Tema
styled-components têm suporte completo a temas exportando um componente wrapperThemeProvider
. Este componente fornece um tema para todos os componentes React dentro de si por meio da API Context. Na árvore de renderização, todos os componentes com estilo terão acesso ao tema fornecido, mesmo quando estiverem em vários níveis de profundidade. À medida que continuarmos neste tutorial, examinaremos mais profundamente os recursos de tema dos componentes com estilo.
Para saber mais vantagens dos componentes com estilo, confira o artigo de Kris Guzman.
Implementando o modo escuro
Neste artigo, vamos implementar o modo escuro em uma página da Web simples semelhante ao YouTube.
Para acompanhar, certifique-se de clonar o repositório original da ramificação starter
.
Configurando
Vamos instalar todas as dependências em nosso arquivo package.json
. No terminal, execute o seguinte comando:
npm install
Após a instalação bem-sucedida, execute npm start
. Aqui está a aparência da página da Web sem o modo escuro implementado nela.

Para instalar styled-components
, em seu terminal, execute npm install styled-components
.
Implementação
Para implementar o modo escuro, precisamos criar quatro componentes diferentes.
-
Theme
Isso contém as propriedades de cor de nossos temas claros e escuros. -
GlobalStyles
Ele contém os estilos globais para todo o documento. -
Toggler
Isso contém o elemento de botão que alterna a funcionalidade. -
useDarkMode
Este hook customizado lida com a lógica por trás da mudança de tema e a persistência do nosso tema em localStorage.
Componente do tema
Na pasta src
, você verá componentes na pasta de components
. Crie um arquivo Themes.js
e adicione o código a seguir a ele.
export const lightTheme = { body: '#FFF', text: '#363537', toggleBorder: '#FFF', background: '#363537', } export const darkTheme = { body: '#363537', text: '#FAFAFA', toggleBorder: '#6B8096', background: '#999', }
Aqui, definimos e exportamos objetos lightTheme
e darkTheme
com variáveis de cores distintas. Sinta-se à vontade para experimentar e personalizar as variáveis para se adequar a você.
componente globalStyles
Permanecendo na pasta de components
, crie um arquivo globalStyles.js
e adicione o seguinte 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; } `
Importamos createGlobalStyle
de styled-components. O método createGlobalStyle
substitui o agora obsoleto método injectGlobal de styled-components versão 3. Esse método gera um componente React, que, quando adicionado à sua árvore de componentes, injetará estilos globais no documento, no nosso caso, App.js
.
Definimos um componente GlobalStyle
e atribuímos propriedades de background
e color
aos valores do objeto de tema. Assim, toda vez que alternarmos a alternância, os valores mudarão dependendo do tema escuro ou dos objetos de tema claro que estamos passando para ThemeProvider
(que será criado posteriormente, à medida que avançamos).
A propriedade de transição de 0.50s
permite que essa mudança ocorra um pouco mais suavemente, de modo que, conforme alternamos para frente e para trás, podemos ver as mudanças acontecerem.
Criando a funcionalidade de alternância de temas
Para implementar a funcionalidade de alternância de temas, precisamos adicionar apenas algumas linhas de código. No arquivo App.js
, adicione o seguinte código (observe que o código destacado é o que você deve adicionar):
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;
O código destacado é aquele recém-adicionado ao App.js
. Importamos ThemeProvider
de styled-components
. ThemeProvider
é um componente auxiliar na biblioteca de componentes com estilo que fornece suporte a temas. Este componente auxiliar injeta um tema em todos os componentes React abaixo de si mesmo por meio da API Context.
Na árvore de renderização, todos os componentes com estilo terão acesso ao tema fornecido, mesmo quando estiverem em vários níveis de profundidade. Confira a seção sobre “Temática”.
Em seguida, importamos o wrapper GlobalStyle
de ./components/Globalstyle
. Por fim, de cima, importamos os objetos lightTheme
e darkTheme
de ./components/Themes
.

Para que possamos criar um método de alternância, precisamos de um estado que contenha o valor de cor inicial do nosso tema. Então, criamos um estado de theme
e definimos o estado inicial como light
, usando o gancho useState
.
Agora, para a funcionalidade de alternância.
O método themeToggler
usa um operador ternário para verificar o estado do theme
e alterna entre escuro ou claro com base no valor da condição.
ThemeProvider
, um componente auxiliar de componentes com estilo, envolve tudo na instrução de return
e injeta quaisquer componentes abaixo dela. Lembre-se de que nossos GlobalStyles
injetam estilos globais em nossos componentes; portanto, é chamado dentro do componente wrapper ThemeProvider
.
Por fim, criamos um botão com um evento onClick
que atribui nosso método themeToggler
a ele.
Vamos ver o resultado até agora.

Nosso arquivo App.js
precisa ser refatorado; muito de seu código não é DRY. (DRY significa “não se repita”, um princípio básico de desenvolvimento de software destinado a reduzir a repetição.) Toda a lógica parece estar em App.js
; é uma boa prática separar nossa lógica por uma questão de clareza. Então, vamos criar um componente que lida com a funcionalidade de alternância.
Alternar componente
Ainda dentro da pasta de components
, crie um arquivo Toggler.js
e adicione o seguinte código a ele:
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 manter as coisas organizadas, estilizamos nosso botão de alternância no componente Toggle
, usando a função styled
de styled-components.
Isto é puramente para apresentação; você pode estilizar o botão como achar melhor.
Dentro do componente Toggle
, passamos dois props:
- o
theme
fornece o tema atual (claro ou escuro); - a função
toggleTheme
será usada para alternar entre os temas.
Em seguida, retornamos o componente Button
e atribuímos uma função toggleTheme
ao evento onClick
.
Por fim, usamos propTypes
para definir nossos tipos, garantindo que nosso theme
seja uma string
e isRequired
, enquanto nosso toggleTheme
é func
e isRequired
.
Usando ganchos personalizados ( useDarkMode
)
Ao construir uma aplicação, a escalabilidade é primordial, o que significa que nossa lógica de negócios deve ser reutilizável, para que possamos usá-la em muitos lugares e até em diferentes projetos.
É por isso que seria ótimo mover nossa funcionalidade de alternância para um componente separado. Para isso, criaríamos nosso próprio gancho personalizado.
Vamos criar um novo arquivo chamado useDarkMode.js
na pasta de components
e mover nossa lógica para este arquivo, com alguns ajustes. Adicione o seguinte código ao arquivo:
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] };
Adicionamos algumas coisas aqui.
-
setMode
UsamoslocalStorage
para persistir entre as sessões no navegador. Portanto, se um usuário escolheu o tema escuro ou claro, é isso que ele obterá na próxima visita ao aplicativo ou se recarregar a página. Portanto, essa função define nosso estado e passa otheme
paralocalStorage
. -
themeToggler
Esta função usa um operador ternário para verificar o estado do tema e alterna entre escuro ou claro com base na veracidade da condição. -
useEffect
Implementamos o ganchouseEffect
para verificar a montagem do componente. Se o usuário já selecionou um tema, vamos passá-lo para nossa funçãosetTheme
. Ao final, retornaremos nossotheme
, que contém otheme
escolhido e a funçãothemeToggler
para alternar entre os modos.
Acho que você concordará que nosso componente de modo escuro parece elegante.
Vamos para App.js
para os retoques finais.
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;
O código destacado foi adicionado recentemente ao App.js
.
Primeiro, importamos nosso gancho personalizado, desestruturamos o theme
e as props themeToggler
e o configuramos com a função useDarkMode
.
Observe que o método useDarkMode
substitui nosso estado de theme
, que estava inicialmente em App.js
.
Declaramos uma variável themeMode
, que renderiza um tema claro ou escuro com base na condição do modo de theme
no momento.
Agora, nosso componente wrapper ThemeProvider
é atribuído à nossa variável themeMode
recém-criada para a prop do theme
.
E por último, no lugar do botão normal, passamos o componente Toggle
.
Lembre-se de que em nosso componente Toggle
, definimos e estilizamos um botão e passamos o theme
e o toggleTheme
para eles como adereços. Então, tudo o que temos a fazer é passar essas props apropriadamente para o componente Toggle
, que funcionará como nosso botão no App.js
.
Sim! Nosso modo escuro está definido e persiste, não mudando de cor quando a página é atualizada ou visitada em uma nova guia.
Vamos ver o resultado em ação:

Quase tudo funciona bem, mas há uma pequena coisa que podemos fazer para tornar nossa experiência esplêndida. Mude para o tema escuro e recarregue a página. Você vê que a cor azul no botão carrega antes do cinza por um breve momento? Isso acontece porque nosso hook useState
inicia o tema light
inicialmente. Depois disso, useEffect
é executado, verifica localStorage
e só então define o theme
como dark
. Vamos pular para o nosso hook customizado useDarkMode.js
e adicionar um pequeno 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
]
};
O código destacado é o único adicionado a useDarkMode.js
. Criamos outro estado chamado mountedComponent
e definimos o valor padrão como false
usando o gancho useState
. Em seguida, dentro do gancho useEffect
, definimos o estado mountedComponent
como true
usando setMountedComponent
. Por último, no array de return
, incluímos o estado do mountedComponent
.
Por fim, vamos adicionar um pouco de código no App.js
para que tudo 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;
Adicionamos nosso estado mountedComponent
como prop em nosso gancho useDarkMode
e verificamos se nosso componente foi montado, porque é isso que acontece no gancho useEffect
. Se ainda não aconteceu, renderizaremos uma div
vazia.
Vamos ver o resultado da nossa página da Web em modo escuro.

Agora, você notará que enquanto no modo escuro, quando a página é recarregada, a cor do botão não muda.
Conclusão
O modo escuro está se tornando cada vez mais uma preferência do usuário, e implementá-lo em um aplicativo Web React é muito mais fácil ao usar o wrapper de temas ThemeProvider
em componentes com estilo. Vá em frente e experimente os componentes com estilo ao implementar o modo escuro; você pode adicionar ícones em vez de um botão.
Por favor, compartilhe seus comentários e experiência com o recurso de temas em componentes de estilo na seção de comentários abaixo. Eu adoraria ver o que você inventa!
O repositório de suporte para este artigo está disponível no GitHub. Além disso, confira no CodeSandbox.
Referências
- “Documentação”, componentes com estilo
- “Crie um modo escuro do seu aplicativo usando componentes com estilo”, Tom Nolan, Medium