Implementazione della modalità oscura nelle app React utilizzando componenti di stile

Pubblicato: 2022-03-10
Riepilogo rapido ↬ La modalità Luce è una convenzione nella maggior parte delle app Web e mobili. Tuttavia, nello sviluppo moderno, abbiamo visto come la modalità oscura, che mostra testo chiaro ed elementi dell'interfaccia su uno sfondo scuro, stia rapidamente diventando una preferenza dell'utente. In questo articolo, impareremo come implementare in modo efficiente la modalità oscura in un'app React su una semplice pagina Web, utilizzando la libreria dei componenti in stile e sfruttando alcune funzionalità di React come gli hook. Discuteremo anche i pro ei contro della modalità oscura e perché dovrebbe essere adottata.

Una 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.

StackOverflow annuncia la modalità oscura su Twitter
StackOverflow annuncia la modalità oscura su Twitter (anteprima grande)

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à.

Altro dopo il salto! Continua a leggere sotto ↓

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.

Twitter modalità scura contro luce
La bellezza della modalità oscura di Twitter rispetto alla modalità luce (anteprima grande)

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 parametro props , 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 wrapper ThemeProvider . 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.

La pagina web da utilizzare, senza modalità oscura
La pagina web da utilizzare, senza modalità oscura. (Grande anteprima)

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.

Modalità oscura implementata senza persistenza.
Modalità oscura implementata senza persistenza (Anteprima grande)

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
    Usiamo localStorage 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 il theme a localStorage .
  • 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 gancio useEffect per verificare il montaggio dei componenti. Se l'utente ha precedentemente selezionato un tema, lo passeremo alla nostra funzione setTheme . Alla fine, restituiremo il nostro theme , che contiene il theme scelto e la funzione themeToggler 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:

Modalità oscura implementata, ma con un problema tecnico nel colore dei pulsanti quando il browser si ricarica.
Modalità oscura implementata, ma con un problema tecnico nel colore dei pulsanti quando il browser si ricarica. (Grande anteprima)

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.

Risultato finale della modalità oscura
Risultato finale della modalità oscura (Anteprima grande)

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