Light ☀️ e Dark 🌙 mode de maneira fácil com Chakra UI, Next.js e Typescript
🤓 Aprenda em menos de 10 minutos como habilitar light e dark mode de forma global na sua aplicação React usando Next.js e Chakra UI.
Tabela de conteúdos
- Introdução;
- O que você vai aprender hoje;
- Pré-requisitos;
- Iniciando um projeto com Next.js;
- Limpando o projeto;
- Instalando e conectando Chakra UI ao seu projeto;
- Criando o nosso primeiro componente com Chakra UI;
- Transformando o nosso componente no botão de alterar tema;
- Utilizando o botão em outras páginas;
- Próximos passos e desafios;
- Código fonte do projeto;
Introdução
Nesse post você irá aprender como criar uma aplicação web usando Next.js e Chakra UI com light ☀️ e dark 🌙 mode. Além de ser muito mais bonito e estar na moda, o dark mode melhora a acessibilidade do seu site para usuários com sensibilidade nos olhos. A aplicação irá usar:
- Next.js como um framework React;
- Chakra UI como biblioteca de componentes React;
O que você vai aprender hoje
- Iniciar e configurar um projeto Next.js com Typescript;
- Instalar e configurar Chakra UI no projeto Next.js;
- Utilizar os React hooks do Chakra UI para lidar e alterar com os
colorModes
(dark mode e light mode); - Criar um componente para alterar o
colorMode
da sua aplicação compartilhável por todo o app;
Pré-requisitos
O que você precisa para acompanhar bem o post;
- Conhecimentos básicos de Node.js;
- Conhecimentos básicos de React;
- Node.js instalado na sua máquina;
Além disto é bom ter um editor para trabalhar com o código como VS Code.
Iniciando um projeto com Next.js
Para iniciar um projeto com Next.js vamos usar o create-next-app
. Já iremos começar usando Typescript para ter toda a ajuda da sua InteliSense.
npx create-next-app@latest <nome-do-projeto> --ts
ou
yarn create next-app <nome-do-projeto> --ts
No meu caso, estou usando yarn
e o nome do meu projeto será light-dark-mode-chakraui
. Logo irei rodar no meu terminal:
yarn create next-app light-dark-mode-chakraui --ts
Isso irá criar uma pasta chamada light-dark-mode-chakraui
dentro do meu diretório atual. Estou usando o VSCode como editor de código, então vou rodar:
# entrar na pasta do projeto
cd light-dark-mode-chakraui
# abrir o projeto no vscode
code .
Estrutura do projeto que o
create-next-app
gera pra gente na sua versão 12.0.4, que foi a que eu usei nesse tutorial.
Abrindo o terminal (no VS Code o comando é ^ + ` ) e rodando o comando yarn dev (ou npm run dev), poderemos acessar o endereço localhost:3000 e teremos esse resultado:
Se você tiver algum erro tentando rodar o comando
dev
, tente atualizar o seu node para a versãolts
, isso deve resolver. Esse link tem as instruções. Qualquer outro problema pode deixar nos comentários desse post que eu ajudo
Limpando o projeto
Vamos limpar o projeto para poder começar do zero um novo, com a nossa cara. Os passos serão esses:
- Remover a pasta
./styles
; - Remover o arquivo
[README.MD](http://README.MD)
→ depois podemos criar outro com a nossa cara; - Remover a pasta
./pages/api
→ não usaremos nesse tutorial; - Criar uma pasta
src
na raiz do projeto;mkdir src
é o comando caso queira criar pelo terminal;
- Mover a pasta
pages
para dentro da pastasrc
; No arquivo
_app.tsx
, remover a importação dos estilos, ficando assim:import type { AppProps } from 'next/app' function MyApp({ Component, pageProps }: AppProps) { return <Component {...pageProps} /> } export default MyApp
Remover todo o conteúdo do arquivo
index.tsx
, deixando ele assim:
import type { NextPage } from 'next'
const Home: NextPage = () => {
return (
<div>
Hello World!
</div>
)
}
export default Home
Agora o seu [http://localhost:3000/](http://localhost:3000/)
deve estar com essa cara:
Instalando e conectando Chakra UI ao projeto
Para instalar o Chakra UI e suas dependências, vamos rodar o seguinte comando:
npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4
ou
yarn add @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4
Agora, vamos conectar o Chakra dentro do Next.js. Para isso, vamos no arquivo src/pages/_app.tsx
e deixa-lo assim:
import type { AppProps } from 'next/app'
// Importar o ChakraProvider
import { ChakraProvider } from "@chakra-ui/react"
function MyApp({ Component, pageProps }: AppProps) {
return (
// Envolver o app com o ChakraProvider
<ChakraProvider>
<Component {...pageProps} />
</ChakraProvider>
)
}
export default MyApp
Agora nós já podemos usar os componentes do Chakra por toda a nossa aplicação.
Criando o nosso primeiro componente com Chakra UI
Vamos criar um componente de Botão (que será o nosso botão de mudança de tema), utilizando o Chakra UI. Para isso, vamos usar o componente IconButton
, de dentro do Chakra UI. Você pode ver a documentação dele aqui. Vamos usar também a biblioteca [react-icons](https://react-icons.github.io/react-icons/)
para pegarmos os nossos ícones.
Primeiro, vamos instalar o react-icons
:
npm install react-icons --save
ou
yarn add react-icons
Agora, vamos criar a pasta components
dentro de src
. Dentro dela, vamos criar o arquivo ToogleThemeButton.tsx
. O código desse arquivo ficará assim:
import { IconButton } from "@chakra-ui/react";
// Importação do icone de dentro do react-icons
import { RiMoonLine } from "react-icons/ri";
export function ToogleThemeButton() {
return (
<IconButton
// Para questões de acessibilidade, explicar o que o elemento faz
aria-label="Toogle theme"
// Icone do botão
icon={<RiMoonLine />}
// A função que será executada quando o botão for clicado
onClick={() => console.log('Clicou no botão')}
/>
);
}
Dica: o pacote
react-icons
tem várias bibliotecas de pacotes conhecidas. Procure utilizar apenas uma biblioteca por projeto, assim o seu projeto fica mais clean. Nesse projeto usaremos a biblioteca de íconesRemix Icons
.
Agora, vamos importar e usar esse componente na nossa home page. O nosso arquivo src/pages/index.tsx
ficará assim:
import { Flex } from '@chakra-ui/react'
import type { NextPage } from 'next'
import { ToogleThemeButton } from '../components/ToogleThemeButton'
const Home: NextPage = () => {
return (
// Ao invés de usarmos o elemento html div,
// usaremos o componente Flex do Chakra UI, que é um container flexbox
<Flex
// Altura da container
height="100vh"
// Largura do container
width="100vw"
// Alinhamento vertical do container
align="center"
// Alinhamento horizontal do container
justify="center"
>
<ToogleThemeButton />
</Flex>
)
}
export default Home
E a nossa página ficará assim:
Transformando o nosso componente no botão de alterar tema
Para que o nosso componente tenha acesso ao tema da nossa aplicação inteira temos que fazer algumas coisas. A primeira é criar uma pasta styles
, dentro de src
, para podermos criar o arquivo do nosso tema lá. Então vamos criar o arquivo src/styles/theme.ts
. Dentro desse arquivo nós vamos extender o tema padrão do Chakra UI e ir alterando ele com o que queremos. O arquivo ficará assim:
import { extendTheme } from '@chakra-ui/react'
export const theme = extendTheme({
// Podemos definir cores que ficarão disponíveis para uso em todo o projeto
colors: {
success: {
'100': '#EBFCD8',
'300': '#B0EF88',
'500': '#5DCC39',
'700': '#28921C',
'900': '#0A610E',
},
danger: {
'100': '#FFE7D9',
'300': '#FFA48D',
'500': '#FF4842',
'700': '#B72136',
'900': '#7A0C2E',
},
warning: {
'100': '#FFF6CE',
'300': '#FFDD6D',
'500': '#FFB80C',
'700': '#B77906',
'900': '#7A4802',
},
},
// Podemos definir a fonte do nosso projeto
fonts: {
heading: 'Be Vietnam',
body: 'Be Vietnam',
},
// Podemos definir um colorMode inicial (escolhi o dark 🌙 como padrão )
config: {
initialColorMode: 'dark',
useSystemColorMode: false,
},
})
Para que essas alterações fiquem visíveis em todo o projeto, temos que adicionar o theme
ao nosso ChakraProvider
, dentro do _app.tsx
:
import type { AppProps } from 'next/app'
// Importar o ChakraProvider
import { ChakraProvider } from "@chakra-ui/react"
import { theme } from '../styles/theme';
function MyApp({ Component, pageProps }: AppProps) {
return (
// Envolver o app com o ChakraProvider
<ChakraProvider resetCSS theme={theme}>
{/* ☝🏽 adicionei o theme a propriedade theme do ChakraProvider */}
<Component {...pageProps} />
</ChakraProvider>
)
}
export default MyApp
Vamos usar algumas coisas que definimos na tema na nossa Home para vermos isso tudo sendo colocado em prática. O arquivo /src/pages/index.tsx
ficará assim:
import { Flex, Text } from '@chakra-ui/react'
import type { NextPage } from 'next'
import { ToogleThemeButton } from '../components/ToogleThemeButton'
const Home: NextPage = () => {
return (
// Ao invés de usarmos o elemento html div,
// usaremos o componente Flex do Chakra UI, que é um container flexbox
<Flex
// Altura da container
height="100vh"
// Largura do container
width="100vw"
// Alinhamento vertical do container
align="center"
// Alinhamento horizontal do container
justify="center"
// Direção do container
flexDirection="column"
>
<Text
// Essa cor vem direto do nosso tema
color="success.700"
// Tamanho da fonte
fontSize="4xl"
// Peso da fonte
fontWeight="bold"
// Alinhamento da fonte
textAlign="center"
>
Botão para mudar tema da aplicação
</Text>
<ToogleThemeButton />
</Flex>
)
}
export default Home
A nossa home deve ficar assim:
Repare que a nossa fonte não mudou. Isso é porque precisamos importar a nossa fonte (usarei o Google Fonts para isso) dentro do nosso projeto. Como o carregamento de uma fonte externa é algo que vamos fazer uma única vez na minha aplicação, eu não vou usar o _app.tsx
pois ele é recarregado toda a vez que o usuário troca de página. Para fazermos isso, criaremos o arquivo _document.tsx
dentro da pasta pages
. Ele ficará assim:
import Document, { Html, Head, Main, NextScript } from 'next/document'
export default class MyDocument extends Document {
render() {
return (
<Html>
<Head>
{/* 👇🏽 definição do favicon */}
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🌙</text></svg>" />
{/* 👇🏽 importação da fonte */}
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Be+Vietnam:wght@100;300;400;500;600;700;800&display=swap"
rel="stylesheet"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
Agora a nossa fonte foi importada corretamente:
Agora podemos finalizar o nosso botão. Vamos importar o React hook useColorMode
que Chakra UI disponibiliza pra gente. Desse hook, vamos importar:
colorMode
→ qual ocolorMode
atual da aplicação;toogleColorMode
→ altera ocolorMode
. SecolorMode === 'light'
altera ocolorMode
paradark
e vice versa.
Além disso, vamos adicionar uma pequena lógica ao nosso componente. Se o colorMode
for light
, eu quero mostrar o ícone da uma Lua 🌙, e se for dark
eu mostro o ícone do Sol ☀️. O componente ficará assim:
import { IconButton, useColorMode } from "@chakra-ui/react";
// Importação do icone de dentro do react-icons
import { RiMoonLine, RiSunLine } from "react-icons/ri";
export function ToogleThemeButton() {
const { colorMode, toggleColorMode } = useColorMode();
return (
<IconButton
// Para questões de acessibilidade, explicar o que o elemento faz
aria-label="Toogle theme"
// Icone do botão
icon={colorMode === 'light' ? <RiMoonLine /> : <RiSunLine />}
// A função que será executada quando o botão for clicado
onClick={toggleColorMode}
/>
);
}
Pronto! Tudo está funcionando:
Utilizando o botão em outras páginas
Para vermos que o colorMode
é compartilhado pela aplicação toda, vamos criar mais uma página, além de alterar um pouco o index.tsx
. Na home, vamos criar um link para a nossa página que vamos criar. Para isso, vamos importar o componente Link
do Next.js. O arquivo index.tsx
vai ficar assim:
import { Flex, Text } from '@chakra-ui/react'
import type { NextPage } from 'next'
import Link from 'next/link'
import { ToogleThemeButton } from '../components/ToogleThemeButton'
const Home: NextPage = () => {
return (
// Ao invés de usarmos o elemento html div,
// usaremos o componente Flex do Chakra UI, que é um container flexbox
<Flex
// Altura da container
height="100vh"
// Largura do container
width="100vw"
// Alinhamento vertical do container
align="center"
// Alinhamento horizontal do container
justify="center"
// Direção do container
flexDirection="column"
>
<Text
// Essa cor vem direto do nosso tema
color="success.700"
// Tamanho da fonte
fontSize="4xl"
// Peso da fonte
fontWeight="bold"
// Alinhamento da fonte
textAlign="center"
>
Botão para mudar tema da aplicação na Home
</Text>
<ToogleThemeButton />
{/* 👇🏽 por questòes de acessibilidade e SEO */}
<Link href="/page2">
<a>
Ir para a página 2
</a>
</Link>
{/* ☝🏽 devemos envolver o elemento filho do componente Link em uma tag 'a' */}
</Flex>
)
}
export default Home;
Agora vamos criar o arquivo /src/pages/page2.tsx
, que será assim:
import { Flex, Text } from '@chakra-ui/react'
import type { NextPage } from 'next'
import Link from 'next/link'
import { ToogleThemeButton } from '../components/ToogleThemeButton'
const Page2: NextPage = () => {
return (
<Flex
height="100vh"
width="100vw"
align="center"
justify="center"
flexDirection="column"
>
<Text
color="success.700"
fontSize="4xl"
fontWeight="bold"
textAlign="center"
>
Botão para mudar tema da aplicação na Página 2
</Text>
<ToogleThemeButton />
<Link href="/">
<a>
Ir para a Home
</a>
</Link>
</Flex>
)
}
export default Page2;
O resultado será esse:
Próximos passos e desafios
Parabéns! Se você chegou até aqui você já sabe criar uma aplicação com light e dark mode! 🎉 🥳
Agora, sugiro que você tente adicionar algumas novas features para aprimorar suas habilidades:
- Altere o título do site que aparece na aba do navegador;
- Utilize o tema do Chakra UI para criar componentes que sejam responsivos entre light e dark mode;
- Crie novas versões (como por exemplo, novos
sizes
ouvariants
) para os componentes do Chakra UI; - Fazer deploy do projeto (eu indico a Vercel);
Link para documentação do Chakra Ui em como customizar componentes.
Link para documentação do deploy na Vercel
Além disso, não se esqueça de postar o resultado no Github. Lembre-se de caprichar no README do projeto!
Código fonte
Aqui está o código fonte da aplicação criada nesse tutorial.