🎨 Light mode e Dark mode e temas customizados com Styled-components e NextJS com Typescript.
Tags: typescript, react, nextjs, design
Published: 2021-01-31 15:56:19

## Introdução as tecnologias

### [NextJS](https://nextjs.org/)

- NextJS é um framework React com o intuito de SSR (Server Side Rendering).

### [Styled-components](https://styled-components.com/)

- Styled-components é uma biblioteca para React que permite a escrita de CSS in JS.

## Vamos começar!

### Criando a aplicação

- Para criar a aplicação iremos rodar o comando `yarn create next-app <nome do projeto>`, após executar esse comando ele irá começar a criar o seu projeto **NextJs** (pode demorar um pouquinho).

### Estruturando a aplicação

- Eu divido a minha estrutura do **NextJS** criando uma pasta `src` e colocando as minhas pastas e arquivos lá, apenas deixando a pasta `public` de fora, mas isso vai de acordo com o seu gosto!

### Configurando o Typescript

- Para configurar o Typescript no projeto é bem simples, iremos adicioná-lo como dependência de desenvolvimento no projeto `yarn add typescript -D`
- Agora iremos criar o arquivo `tsconfig.json`, no seu terminal escreva `touch tsconfig.json`
- Iremos modificar o nome das nossas páginas dentro de `src/pages` removendo a extensão `.js` e adicionando `.tsx`
- E se tudo deu certo ~~pelo menos eu espero que sim~~, podemos rodar `yarn dev` no nosso terminal e abrirá a página do **NextJS** na porta `:3000`

![Estrutura de pastas](https://dev-to-uploads.s3.amazonaws.com/i/dmjbxl5gahx020cgrppm.png)

### Configurando o Styled-components

- Vamos começar adicionado o styled-components no projeto, portanto escreve no seu terminal `yarn add styled-components`
- Iremos criar um arquivo chamado `_document.tsx` dentro de `src/pages`, nele haverá o seguinte conteúdo. **Isso é para as injeções de estilo no server side rendering.**

    ```jsx
    import Document, { DocumentContext } from 'next/document';
    import { ServerStyleSheet } from 'styled-components';

    export default class MyDocument extends Document {
      static async getInitialProps(ctx: DocumentContext) {
        const sheet = new ServerStyleSheet();
        const originalRenderPage = ctx.renderPage;

        try {
          ctx.renderPage = () =>
            originalRenderPage({
              enhanceApp: App => props => sheet.collectStyles(<App {...props} />),
            });

          const initialProps = await Document.getInitialProps(ctx);
          return {
            ...initialProps,
            styles: (
              <>
                {initialProps.styles}
                {sheet.getStyleElement()}
              </>
            ),
          };
        } finally {
          sheet.seal();
        }
      }
    }
    ```

- Dentro da nossa pasta `src/styles` vamos criar o arquivo `styled.d.ts` para sobrescrevermos os tipos do **styled-components.**

    ```jsx
    import 'styled-components';

    declare module 'styled-components' {
      export interface DefaultTheme {
        title: string;
        colors: {
          primary: string;
          secundary: string;

          background: string;
          text: string;
        };
        fontSizes: {
          small: string;
          medium: string;
          large: string;
        };
      }

      export interface CustomTheme {
        title: string;
        colors: {
          primary: string;
          secundary: string;

          background: string;
          text: string;
        };
      }
    }
    ```

- Dentro no nosso arquivo tsconfig.json vamos adicionar o seguinte atributo.

    ```json
    ....
    "files": [
        "src/styles/styled.d.ts"
      ]
    ```

- Vamos rapidinho criar um estilo global para a aplicação, crie o arquivo global.ts dentro de `src/styles`

    ```jsx
    import { createGlobalStyle } from 'styled-components';

    export default createGlobalStyle`
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
        outline: 0;
      }

      body {
        background: #fff;
        color: black;
      }

      h1, h2, h3, h4, h5, h6, strong {
        font-weight: 700;
      }

      button {
        cursor: pointer;
      }
    `;
    ```

- Agora vamos importá-lo no arquivo `index.tsx`, delete os arquivos de estilo `.css` do projeto também!

    ```jsx
    import GlobalStyles from '../styles/global';

    export default function Home() {
      return (
        <>
          <GlobalStyles />
        </>
      );
    }
    ```

### Criando os temas

- Vamos criar os temas agora! Dentro da pasta `src/styles` vamos criar uma pasta `themes` e nela um arquivo dark.ts (para o dark mode) o arquivo light.ts para o (light mode ~~jura?~~) e um arquivo index.ts.
- O arquivo `dark.ts` terá as seguintes cores (isso você decide de acordo com o seu design)

    ```jsx
    export default {
      title: 'dark',

      colors: {
        primary: '#161616',
        secundary: '#555',

        background: '#333',
        text: '#fff',
      },
    };
    ```

- O arquivo `light.ts` terá as seguintes cores

    ```jsx
    export default {
      title: 'light',

      colors: {
        primary: '#666',
        secundary: '#777',

        background: '#fff',
        text: '#333',
      },
    };
    ```

- O arquivo `index.ts` será responsável por misturar as cores de cada tema com o que seria comum entre os dois,  **exemplo: tamanho de fonte.**

    ```jsx
    import { DefaultTheme, CustomTheme } from 'styled-components';
    import dark from './dark';
    import light from './light';

    const defaultTheme = {
      fontSizes: {
        small: '16px',
        medium: '18px',
        large: '20px',
      },
    };

    function combineTheme(theme: CustomTheme): DefaultTheme {
      return { ...defaultTheme, ...theme };
    }

    export { combineTheme, dark, light };
    ```

### Aplicando os temas!

- Agora que já criamos nossos temas vamos importá-los e fazer as trocas dinâmicas de temas, em `src/pages` vamos fazer algumas alterações no nosso `index.tsx`, mas antes vamos adicionar um componente de switch para ficar mais estiloso já que estamos falando de temas, portanto, escreve `yarn add react-switch` no seu terminal

    ```jsx
    import React, { useState } from 'react';
    import { DefaultTheme, ThemeProvider } from 'styled-components';
    import Switch from 'react-switch';
    import { combineTheme, dark, light } from '../styles/themes';
    import GlobalStyles from '../styles/global';

    const Home: React.FC = () => {
      const [theme, setTheme] = useState<DefaultTheme>(combineTheme(light));

      const toggleTheme = () => {
        setTheme(theme.title === 'light' ? combineTheme(dark) : combineTheme(light));
      };

      return (
        <ThemeProvider theme={theme}>
          <GlobalStyles />
          <Switch checked={theme.title === 'dark'} onChange={toggleTheme} />
        </ThemeProvider>
      );
    };

    export default Home;
    ```

- Agora vamos em `src/styles` no nosso arquivo `global.ts` e vamos fazer as seguintes alterações!

    ```jsx
    import { createGlobalStyle } from 'styled-components';

    export default createGlobalStyle`
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
        outline: 0;
      }

      body {
        background: ${props => props.theme.colors.background};
        color: ${props => props.theme.colors.text};
        font-size: ${props => props.theme.fontSizes.small}
      }

      h1, h2, h3, h4, h5, h6, strong {
        font-weight: 700;
      }

      button {
        cursor: pointer;
      }
    `;
    ```

- E pronto!!! O resultado final ficará assim

![Exemplo GIF](https://dev-to-uploads.s3.amazonaws.com/i/ci5k2cohw4a3ataxxh60.gif)

**[Projeto no Github](https://github.com/jjairojr/custom-themes)
[Linkedin](https://www.linkedin.com/in/jjairo/)**