Como implementar modo dark em um projeto nextjs e tailwindcss

No primeiro post mostrei como este blog foi implementado a partir de um exemplo oficial do nextjs e hospedado no github pages. Agora preciso implementar o modo dark nele para conseguirmos ler o conteúdo sem prejudicar nossa visão. hoje implementaremos um botão com ícones de sol/lua para alternar entre os modos dark e light.

Usaremos o next-themes que já nos fornece o funcionamento dos modos dark/light e o react-icons apenas para inserir os ícones de sol e lua.

npm install next-themes react-icons

ou

yarn add next-themes react-icons

instalando o ThemeProvider

No pages/_app.tsx deveremos importar o ThemeProvider e usa-lo para envolver o

Para habilitar o modo dark como padrão insira o atributo defaultTheme="dark" no ThemeProvider

O atributo attribute="class" é obrigatório para fazer funcionar com as classes do tailwind

import { ThemeProvider } from "next-themes";
import { AppProps } from 'next/app'
import '../styles/index.css'


export default function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ThemeProvider defaultTheme="dark" attribute="class">
      <Component {...pageProps} />
    </ThemeProvider>
    );
}

Habilitando o modo dark

No arquivo tailwind.config.js insira a linha darkMode: 'class', para habilitar o modo dark

module.exports = {
  content: ['./components/**/*.tsx', './pages/**/*.tsx'],
  darkMode: 'class',

Definindo as classes css de textos e background

Setaremos as cores do texto e backgrounds em cada modo. No arquivo styles/index.css acrescente o seguinte código

@layer base {
    body {
        @apply bg-gray-50
        dark:bg-gray-900
        text-gray-900
        dark:text-gray-50
        transition-colors;
    }
}

Componente para alternar modos.

Crie um componente chamado components/themeToggle com o seguinte conteúdo:

import { useEffect, useState } from "react";
import { useTheme } from "next-themes";

// Aqui temos os ícones de Lua e Sol
import { BiMoon, BiSun } from "react-icons/bi";

export default function ThemeToggle() {
    const [mounted, setMounted] = useState(false);
    const { theme, setTheme } = useTheme();

    useEffect(() => setMounted(true), []);
    if (!mounted) return null;

    function isDark() {
        return theme === "dark";
    }

    return (
        <button
            className="focus:outline-none"
            onClick={() => setTheme(isDark() ? "light" : "dark")}
            aria-label="Theme toggle"
        >
            {isDark() ? <BiSun size={20} /> : <BiMoon size={20} />}
        </button>
    );
}

Agora é só importa-lo e usa-lo em qualquer lugar. aqui no blog coloquei no components/layout.tsx para ficar presente em todas as telas

<div className="p-3 fixed right-0 top-0">
  <ThemeToggle />
</div>

Posts