Implementazione della modalità oscura nelle app React utilizzando componenti di stile
Pubblicato: 2022-03-10Una delle funzionalità software più comunemente richieste è la modalità oscura (o modalità notturna, come la chiamano altri). Vediamo la modalità oscura nelle app che utilizziamo ogni giorno. Dai dispositivi mobili alle app Web, la modalità oscura è diventata fondamentale per le aziende che vogliono prendersi cura degli occhi dei propri utenti.
La modalità oscura è una funzionalità aggiuntiva che mostra principalmente superfici scure nell'interfaccia utente. La maggior parte delle principali aziende (come YouTube, Twitter e Netflix) ha adottato la modalità oscura nelle proprie app mobili e web.
Anche se non approfondiremo React e i componenti in stile, una conoscenza di base di React, CSS e componenti in stile sarebbe utile. Questo tutorial andrà a beneficio di coloro che stanno cercando di migliorare le proprie applicazioni Web rivolgendosi a coloro che amano la modalità oscura.

Pochi giorni prima della stesura di questo articolo, StackOverflow ha annunciato il rilascio della modalità oscura, dando agli utenti la possibilità di alternare tra le due modalità.
La modalità scura riduce l'affaticamento degli occhi e aiuta quando si lavora a lungo su un computer o un telefono cellulare.
Cos'è la modalità oscura?
La modalità scura è la combinazione di colori di qualsiasi interfaccia che visualizza il testo chiaro e gli elementi dell'interfaccia su uno sfondo scuro, il che rende lo schermo un po' più facile da guardare su telefoni cellulari, tablet e computer. La modalità Scuro riduce la luce emessa dallo schermo, pur mantenendo i rapporti di contrasto colore minimi richiesti per la leggibilità.
Perché dovresti preoccuparti della modalità oscura?
La modalità scura migliora l'ergonomia visiva riducendo l'affaticamento degli occhi, adattando lo schermo alle condizioni di luce attuali e fornendo facilità d'uso di notte o in ambienti bui.
Prima di implementare la modalità oscura nella nostra app, diamo un'occhiata ai suoi vantaggi.
Risparmio batteria
La modalità oscura nelle app Web e mobili può prolungare la durata della batteria di un dispositivo. Google ha confermato che la modalità oscura sugli schermi OLED è stata di grande aiuto per la durata della batteria.
Ad esempio, con una luminosità del 50%, la modalità scura nell'app YouTube consente di risparmiare circa il 15% in più di energia dello schermo rispetto a uno sfondo bianco piatto. Con una luminosità dello schermo del 100%, l'interfaccia scura consente di risparmiare ben il 60% dell'energia dello schermo.
La modalità oscura è bellissima
La modalità oscura è bellissima e può migliorare notevolmente l'attrattiva dello schermo.
Mentre la maggior parte dei prodotti ha un aspetto simile al bianco blando, la modalità oscura offre qualcosa di diverso che sembra misterioso e nuovo.
Fornisce inoltre grandi opportunità per presentare contenuti grafici come dashboard, immagini e foto in un modo nuovo.

Ora che sai perché dovresti implementare la modalità oscura nella tua prossima app Web, immergiamoci nei componenti in stile, che è la risorsa che definisce questo tutorial.
La modalità scura è la combinazione di colori di qualsiasi interfaccia che visualizza il testo chiaro e gli elementi dell'interfaccia su uno sfondo scuro, il che rende un po' più semplice la visualizzazione su telefoni cellulari, tablet e computer.
“
Cosa sono i componenti con stile?
In questo articolo, useremo molto spesso la libreria dei componenti in stile. Ci sono sempre stati molti modi per dare uno stile a un'app Web moderna. Esiste il metodo tradizionale di stile a livello di documento, che include la creazione di un file index.css
e il collegamento all'HTML o lo stile all'interno del file HTML.
Molto è cambiato nel modo in cui le app Web sono state progettate di recente, dall'introduzione di CSS-in-JS.
CSS-in-JS si riferisce a un modello in cui CSS è composto utilizzando JavaScript. Utilizza letterali modello con tag per definire lo stile dei componenti in un file JavaScript.
Per saperne di più su CSS-in-JS, consulta l'articolo di Anna Monus sull'argomento.
styled-components è una libreria CSS-in-JS che ti consente di utilizzare tutte le funzionalità dei CSS che ami, comprese le query multimediali, gli pseudo-selettori e la nidificazione.
Perché i componenti in stile?
styled-components è stato creato per i seguenti motivi:
- Nessun nome di classe inferno
Invece di grattarti la testa per trovare un nome di classe per un elemento, styled-components genera nomi di classe univoci per i tuoi stili. Non dovrai mai preoccuparti di errori di ortografia o di usare nomi di classi privi di significato. - Usando oggetti di scena
styled-components ci consente di estendere le proprietà di stile usando il parametroprops
, comunemente usato in React, influenzando così dinamicamente la sensazione di un componente attraverso lo stato dell'applicazione. - Supporta la sintassi Sass
Scrivere la sintassi Sass fuori dagli schemi senza dover impostare alcun preprocessore o strumenti di compilazione aggiuntivi è possibile con i componenti in stile. Nelle definizioni di stile, puoi utilizzare il carattere&
per scegliere come target il componente corrente, utilizzare pseudo-selettori e sperimentare la nidificazione. - Temi
styled-components ha un supporto completo per i temi esportando un componente wrapperThemeProvider
. Questo componente fornisce un tema a tutti i componenti React al suo interno tramite l'API Context. Nell'albero di rendering, tutti i componenti con stile avranno accesso al tema fornito, anche quando sono profondi più livelli. Continuando in questo tutorial, esamineremo più in profondità le funzionalità dei temi dei componenti con stile.
Per ulteriori informazioni sui vantaggi dei componenti in stile, consulta l'articolo di Kris Guzman.
Implementazione della modalità oscura
In questo articolo, implementeremo la modalità oscura su una semplice pagina Web simile a YouTube.
Per seguire, assicurati di clonare il repository originale dal ramo di starter
.
Impostare
Installiamo tutte le dipendenze nel nostro file package.json
. Dal terminale, esegui il seguente comando:
npm install
Al termine dell'installazione, eseguire npm start
. Ecco come appare la pagina web senza la modalità oscura implementata su di essa.

Per installare styled-components
, nel tuo terminale esegui npm install styled-components
.
Implementazione
Per implementare la modalità oscura, dobbiamo creare quattro diversi componenti.
-
Theme
Questo contiene le proprietà del colore dei nostri temi chiari e scuri. -
GlobalStyles
Questo contiene gli stili globali per l'intero documento. -
Toggler
Questo contiene l'elemento pulsante che attiva o disattiva la funzionalità. -
useDarkMode
Questo hook personalizzato gestisce la logica dietro il cambio di tema e la persistenza del nostro tema in localStorage.
Componente tematica
Nella cartella src
, vedrai i componenti nella cartella dei components
. Crea un file Themes.js
e aggiungi il codice seguente.
export const lightTheme = { body: '#FFF', text: '#363537', toggleBorder: '#FFF', background: '#363537', } export const darkTheme = { body: '#363537', text: '#FAFAFA', toggleBorder: '#6B8096', background: '#999', }
Qui abbiamo definito ed esportato oggetti lightTheme
e darkTheme
con variabili di colore distinte. Sentiti libero di sperimentare e personalizzare le variabili in base alle tue esigenze.
Componente GlobalStyles
Rimanendo nella cartella components
, crea un file globalStyles.js
e aggiungi il codice seguente:
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; } `
Abbiamo importato createGlobalStyle
da componenti con stile. Il metodo createGlobalStyle
sostituisce il metodo injectGlobal ora deprecato di styled-components versione 3. Questo metodo genera un componente React che, una volta aggiunto all'albero dei componenti, inietterà stili globali nel documento, nel nostro caso App.js
.
Abbiamo definito un componente GlobalStyle
e assegnato proprietà di background
e color
ai valori dell'oggetto tema. Pertanto, ogni volta che passiamo all'interruttore, i valori cambieranno a seconda del tema scuro o degli oggetti del tema chiaro che stiamo passando a ThemeProvider
(che verrà creato in seguito, mentre procediamo).
La proprietà di transizione di 0.50s
consente a questo cambiamento di verificarsi in modo un po' più fluido, in modo che mentre ci muoviamo avanti e indietro, possiamo vedere che i cambiamenti accadono.
Creazione di funzionalità di alternanza dei temi
Per implementare la funzionalità di attivazione/disattivazione dei temi, è necessario aggiungere solo poche righe di codice. Nel file App.js
, aggiungi il codice seguente (nota che il codice evidenziato è quello che dovresti aggiungere):
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;
Il codice evidenziato è quello appena aggiunto ad App.js
. Abbiamo importato ThemeProvider
da styled-components
. ThemeProvider
è un componente di supporto nella libreria dei componenti di stile che fornisce supporto per i temi. Questo componente di supporto inserisce un tema in tutti i componenti React sotto se stesso tramite l'API Context.
Nell'albero di rendering, tutti i componenti con stile avranno accesso al tema fornito, anche quando sono profondi più livelli. Dai un'occhiata alla sezione "Tematura".
Successivamente, importiamo il wrapper GlobalStyle
da ./components/Globalstyle
. Infine, dall'alto, importiamo sia gli oggetti lightTheme
che darkTheme
da ./components/Themes
.

Per poter creare un metodo di commutazione, abbiamo bisogno di uno stato che contenga il valore del colore iniziale del nostro tema. Quindi, creiamo uno stato del theme
e impostiamo lo stato iniziale su light
, usando l'hook useState
.
Ora, per la funzionalità di commutazione.
Il metodo themeToggler
usa un operatore ternario per controllare lo stato del theme
e commuta tra scuro o chiaro in base al valore della condizione.
ThemeProvider
, un componente di supporto per i componenti in stile, avvolge tutto nell'istruzione return
e inserisce tutti i componenti sotto di essa. Ricorda che i nostri GlobalStyles
iniettano stili globali nei nostri componenti; quindi, viene chiamato all'interno del componente wrapper ThemeProvider
.
Infine, abbiamo creato un pulsante con un evento onClick
che gli assegna il nostro metodo themeToggler
.
Vediamo il risultato fino ad ora.

Il nostro file App.js
deve essere rifattorizzato; gran parte del suo codice non è DRY. (DRY sta per "non ripetere te stesso", un principio di base dello sviluppo software volto a ridurre le ripetizioni.) Tutta la logica sembra essere in App.js
; è buona norma separare la nostra logica per motivi di chiarezza. Quindi, creeremo un componente che gestisce la funzionalità di commutazione.
Attiva/disattiva componente
Sempre all'interno della cartella dei components
, crea un file Toggler.js
e aggiungi il codice seguente:
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;
Per mantenere le cose in ordine, abbiamo applicato uno stile al nostro pulsante di attivazione/disattivazione nel componente Toggle
/disattiva, utilizzando la funzione di styled
di componenti di stile.
Questo è puramente per la presentazione; puoi modellare il pulsante come meglio credi.
All'interno del componente Toggle
, passiamo due prop:
- il
theme
fornisce il tema corrente (chiaro o scuro); - la funzione
toggleTheme
verrà utilizzata per passare da un tema all'altro.
Successivamente, restituiamo il componente Button
e assegniamo una funzione toggleTheme
all'evento onClick
.
Infine, utilizziamo propTypes
per definire i nostri tipi, assicurandoci che il nostro theme
sia una string
e isRequired
, mentre il nostro toggleTheme
sia func
e isRequired
.
Utilizzo di hook personalizzati ( useDarkMode
)
Quando si costruisce un'applicazione, la scalabilità è fondamentale, il che significa che la nostra logica di business deve essere riutilizzabile, in modo da poterla utilizzare in molti luoghi e anche in progetti diversi.
Ecco perché sarebbe fantastico spostare la nostra funzionalità di attivazione/disattivazione in un componente separato. Per questo, creeremmo il nostro gancio personalizzato.
Creiamo un nuovo file chiamato useDarkMode.js
nella cartella dei components
e spostiamo la nostra logica in questo file, con alcune modifiche. Aggiungi il seguente codice al file:
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] };
Abbiamo aggiunto alcune cose qui.
-
setMode
UsiamolocalStorage
per persistere tra le sessioni nel browser. Quindi, se un utente ha scelto il tema scuro o chiaro, è quello che otterrà alla prossima visita all'app o se ricarica la pagina. Quindi, questa funzione imposta il nostro stato e passa iltheme
alocalStorage
. -
themeToggler
Questa funzione utilizza un operatore ternario per controllare lo stato del tema e alterna oscuro o chiaro in base alla verità della condizione. -
useEffect
Abbiamo implementato il ganciouseEffect
per verificare il montaggio dei componenti. Se l'utente ha precedentemente selezionato un tema, lo passeremo alla nostra funzionesetTheme
. Alla fine, restituiremo il nostrotheme
, che contiene iltheme
scelto e la funzionethemeToggler
per passare da una modalità all'altra.
Penso che sarai d'accordo sul fatto che il nostro componente in modalità oscura sia elegante.
Andiamo su App.js
per gli ultimi ritocchi.
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;
Il codice evidenziato è stato appena aggiunto ad App.js
.
Per prima cosa, importiamo il nostro hook personalizzato, destrutturiamo il theme
e gli oggetti di scena themeToggler
e lo impostiamo con la funzione useDarkMode
.
Si noti che il metodo useDarkMode
sostituisce il nostro stato del theme
, che inizialmente era in App.js
.
Dichiariamo una variabile themeMode
, che esegue il rendering di un tema chiaro o scuro in base alle condizioni della modalità theme
in quel momento.
Ora, al nostro componente wrapper ThemeProvider
viene assegnata la nostra variabile themeMode
appena creata al prop del theme
.
E infine, al posto del pulsante normale, passiamo al componente Toggle
.
Ricorda che nel nostro componente Toggle
, abbiamo definito e stilizzato un pulsante e gli abbiamo passato sia il theme
che toggleTheme
come oggetti di scena. Quindi, tutto ciò che dobbiamo fare è passare questi oggetti di scena in modo appropriato al componente Toggle
, che fungerà da nostro pulsante in App.js
.
Sì! La nostra modalità oscura è impostata e persiste, senza cambiare colore quando la pagina viene aggiornata o visitata in una nuova scheda.
Vediamo il risultato in azione:

Quasi tutto funziona bene, ma c'è una piccola cosa che possiamo fare per rendere splendida la nostra esperienza. Passa al tema scuro e quindi ricarica la pagina. Vedi che il colore blu nel pulsante viene caricato prima del grigio per un breve momento? Ciò accade perché il nostro hook useState
avvia inizialmente il tema della light
. Successivamente, useEffect
viene eseguito, controlla localStorage
e solo allora imposta il theme
su dark
. Passiamo al nostro hook personalizzato useDarkMode.js
e aggiungiamo un piccolo codice:
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
]
};
Il codice evidenziato è l'unico aggiunto a useDarkMode.js
. Abbiamo creato un altro stato chiamato mountedComponent
e impostato il valore predefinito su false
usando l'hook useState
. Successivamente, all'interno useEffect
, impostiamo lo stato mountedComponent
su true
usando setMountedComponent
. Infine, nell'array di return
, includiamo lo stato mountedComponent
.
Infine, aggiungiamo un po' di codice in App.js
per far funzionare tutto.
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;
Abbiamo aggiunto il nostro stato mountedComponent
come supporto nel nostro hook useDarkMode
e abbiamo verificato se il nostro componente è stato montato, perché questo è ciò che accade useEffect
. Se non è ancora successo, renderemo un div
vuoto.
Vediamo il risultato della nostra pagina web in modalità oscura.

Ora noterai che in modalità oscura, quando la pagina si ricarica, il colore del pulsante non cambia.
Conclusione
La modalità oscura sta diventando sempre più una preferenza dell'utente e implementarla in un'app Web React è molto più semplice quando si utilizza il wrapper di temi ThemeProvider
nei componenti con stile. Vai avanti e sperimenta con i componenti in stile mentre implementi la modalità oscura; potresti aggiungere icone invece di un pulsante.
Condividi il tuo feedback e la tua esperienza con la funzione di tematizzazione nei componenti di stile nella sezione commenti qui sotto. Mi piacerebbe vedere cosa ti viene in mente!
Il repository di supporto per questo articolo è disponibile su GitHub. Inoltre, dai un'occhiata su CodeSandbox.
Riferimenti
- “Documentazione”, styled-components
- "Crea una modalità oscura della tua app utilizzando Styled Components", Tom Nolan, Medium