João Pedro
jpc0rrea's blog

jpc0rrea's blog

Light ☀️  e Dark 🌙  mode de maneira fácil com Chakra UI, Next.js e Typescript

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.

João Pedro's photo
João Pedro

Published on Nov 25, 2021

10 min read

Tabela de conteúdos

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 .

inital-structure.png 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:

initial-print-screen.png

Se você tiver algum erro tentando rodar o comando dev, tente atualizar o seu node para a versão lts, 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 pasta src;
  • 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:

clean-structure-print-screen.png

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 ícones Remix 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:

console-log-gif.webp

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:

project-without-font.png

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:

project-with-font.png

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 o colorMode atual da aplicação;
  • toogleColorMode → altera o colorMode. Se colorMode === 'light' altera o colorMode para dark 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:

switch-theme-gif.webp

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:

switch-theme-between-pages-gif.webp

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 ou variants) 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.

 
Share this