DEV Community

Cover image for React - Bora estilizar?
Bruno Felipe
Bruno Felipe

Posted on

React - Bora estilizar?

👋 Introdução

Se você já esteve em algum projeto web com certeza já trabalhou com CSS ou algum dos seus pré-processadores (Sass, LESS, Stylus).
E no React não é diferente. Todas essas tecnologias também podem e devem ser utilizadas, porém vamos começar com o básico e ver como é simples adicionar CSS a um componente.

Primeiro vamos estilizar o Header da nossa aplicação, mas para isso teremos que modificar um pouco nosso arquivo. Precisamos criar uma passar com nome Header, assim como o arquivo Header.tsx. E dentro dessa pasta iremos criar um arquivo index.tsx copiando todo conteúdo que temos no Header.tsx sem mudar nada. Além disso iremos criar ainda dentro dessa pasta um arquivo styles.css para podermos importar dentro do componente.
Por fim apagamos o Header.tsx.

/* components/Header/styles.css */

#header {
  background: #5636d3;
}
Enter fullscreen mode Exit fullscreen mode
// components/Header/index.tsx

import "./styles.css"

function Header() {
  return (
    <div>
      <header id="header">
        <span>gofinances</span>
        <nav>
          <a href="/">Listagem</a>
          <a href="/create">Cadastro</a>
        </nav>
      </header>
    </div>
  );
}

export default Header
Enter fullscreen mode Exit fullscreen mode

Se o seu servidor ainda estiver rodando (se não, npm start) basta entrar no http://localhost:3000 e você verá que a cor de fundo do nosso componente Header está roxo. Simples assim para utilizar CSS no React.

Caso você vá utilizar algum pré-processador, Google é seu melhor amigo, mas já posso te adiantar que não deve ser nenhum monstro de sete cabeças nem nada do tipo, porque no fim é apenas importar o arquivo CSS para dentro do componente React e ele funcionará.

Mas essa abordagem de criar um arquivo CSS para cada componente tem o seu problema.

⁉️ Qual o problema?

Ao separarmos os arquivos CSS em cada componente já é algo muito bom, bem melhor do que ter um arquivo único com toda estilização da nossa aplicação. Porém, ai no seu código faça um teste:

  • Duplique a pasta do componente Header e renomeio para um nome qualquer como Header2.
  • Importe esse componente no App.tsx e adicione-o abaixo <Header />.
  • No arquivo styles.css desse novo componente altere o background para uma cor qualquer (pode ser #000 que é preto).

Se você acompanhou os últimos posts dessa série isso deve ser fácil para você.

Ao entrar no nosso projeto rodando no browser veremos que os dois Headers tem a mesma cor de fundo, a cor que você alterou. Mas porque isso aconteceu se criamos o CSS para cada componente?

Isso acontece porque estamos utilizando o mesmo seletor CSS para estilizar: #header e o React importa os dois arquivos dentro da página, assim o CSS que é carregado por último é o que sobrescreve o outro. Uma solução para isso seria utilizar seletores diferentes, como outro id para o segundo header ou uma classe diferente.
Porém essa abordagem fica bem problemática quando o projeto cresce em escala, já que você não vai lembrar dos nomes que já foram dados anteriormente muito menos saber qual nome outro pessoa que trabalha no projeto utilizou.

E foi pensando nesse caso que vamos utilizar o Styled Components.

📐 Componentes Estilizados

O que Styled Components faz é: ele cria uma classe com identificador único para cada Componente Estilizado que criamos. Assim ele garante que ao criar um nova estilização não vamos estar afetando outro lugar da aplicação. Ele nada mais é do que um criador de CSS escrito em Javascript.
Se só isso não bastasse com ele também podemos criar temas para nossa aplicação de maneira simples e fácil, além de poder criar animações reutilizáveis entre vários componentes. Mas vamos fazer o simples primeiro.

Vamos instalar o pacote no nosso projeto:

npm i styled-components
Enter fullscreen mode Exit fullscreen mode

Como esse pacote não é escrito em Typescript, temos que instalar também as tipagens. O -D serve para adicionarmos essa dependência apenas para o ambiente de desenvolvimento, já que em produção não estaremos utilizando Typescript.

npm i @types/styled-components -D
Enter fullscreen mode Exit fullscreen mode

:pin: Se você estiver utilizando o VSCode, existe uma extensão que nos ajudará ao trabalhar com o styled-components que é essa. Caso não seja o seu caso, na documentação há ferramentas para outros editores de texto.

Agora, vamos alterar o arquivo styles.css para styles.ts e alterar seu conteúdo para:

// components/Header/styles.ts

import styled from 'styled-components'

export const Container = styled.div`
  background: #5636d3;
`
Enter fullscreen mode Exit fullscreen mode

E o que é cada parte desse código?

  • export const Container estamos exportando desse arquivo uma constante que por ser um componente estilizado deve sempre iniciar com letra maiúscula.
  • styled é o nome que damos para a lib, poderia ser batata se quisermos, mas vamos seguir como mostra a documentação.
  • .div indica qual será o retorno desse componente, podendo ser qualquer tag do HTML5.
  • O conteúdo dentro das crases são o CSS em si, que será aplicado a essa div utilizando uma classe única.

⚠️ Caso essa sintaxe pareça diferente para você tem um artigo bem legal da Alura que explica como funciona as Tagged Template Literals.

Para vermos o efeito dessa estilização no código temos que alterar o Header/index.tsx:

// components/Header/index.tsx

import { Container } from "./styles";

function Header() {
  return (
    <Container>
      <header>
        <span>gofinances</span>
        <nav>
          <a href="/">Listagem</a>
          <a href="/create">Cadastro</a>
        </nav>
      </header>
    </Container>
  );
}

export default Header
Enter fullscreen mode Exit fullscreen mode

Vemos que o Container é como qualquer outro componente, porém nele contém apenas estilização e nada mais.

A partir de agora temos duas maneiras de prosseguir, sendo elas:

  • Podemos criar vários componentes estilizados dentro de um componente como o Header. Por exemplo, além do Container, outros como Nav, Title e Link cada um estilizando uma parte do nosso componente.
  • Ou podemos utilizar o encadeamento das tags e ir estilizando tudo pelo componente container. Por exemplo, se quisermos que o <span> tenha uma cor de texto branca poderíamos fazer assim:
// components/Header/styles.ts

import styled from 'styled-components'

export const Container = styled.div`
  background: #5636d3;

  span {
    color: #FFF
  }
`
Enter fullscreen mode Exit fullscreen mode

Vamos seguir o segundo modelo, por praticidade.

♻️ Resetando os estilos globalmente

Muitos das tags do HTML tem uma própria estilização que pode nos atrapalhar. Podemos ver isso no nosso projeto:

Print #1

A tag body tem uma 'margin' para todos os lados de 8px. E para resetar todos essas estilizações vamos criar um pasta styles dentro do src. Dentro dessa pasta, vamos criar um arquivo global.ts para nossa estilização global.

//styles/global.ts

import { createGlobalStyle } from 'styled-components'

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

  body {
    background: #F0F2F5 ;
    -webkit-font-smoothing: antialiased
  }

  body, input, button {
    font: 16px "Poppins", sans-serif;
  }

  button {
    cursor: pointer;
  }
`
Enter fullscreen mode Exit fullscreen mode

Explicando o que esse arquivo faz:

  • Primeiro importamos de dentro do 'styled-components' uma função que cria um estilização global (createGlobalStyle); e assim como fazíamos com uma div, escrevemos nosso código CSS entre crases.

  • No CSS, estamos zerando todos os espaçamentos, mudando a cor de fundo padrão e até mudando a fonte da aplicação. Vale ressaltar a importância de ao alterar a fonte, mudar também na tag input e button, já que em ambos não são aplicados a fonte do body.

  • Por fim, mudamos o cursor padrão de um botão para a mãozinha do click 👆.

Para importamos a fonte 'Poppins' no nosso projeto vamos utilizar o Google Fonts. Como esse processo pode mudar quando você estiver lendo isso vou apenas deixar meu código atual:

<!-- public/index.html -->

<!DOCTYPE html>
<html lang="pt_BR">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <link rel="preconnect" href="https://fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap" rel="stylesheet">  
    <title>Go Finances</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Agora temos que importar styles/global.ts no nosso App.tsx para que ele tenha efeito.

Mas antes, só para facilitar mais pra frente vamos passar parte do nosso código que está no App.tsx para outro componente.

Mas porque temos que criar mais um componente? Não basta deixar tudo dentro do 'App'?

Lembrando uma das vantagens da componentização é a reutilização de código, e já que teremos mais uma página e em todas queremos que o GlobalStyle seja aplicado temos que separar a nossa página principal. E mais uma vez isso é bem simples.

Basta lembrar, sempre que criamos um compomente que terá estilização criamos uma pasta com o nome do componente e dentro criamos um arquivo index.tsx e outro styles.ts,

Criando uma pasta pages dentro de src, e criando um componente Home que contém:

//pages/Home/index.tsx

import Header from "../../components/Header";
import TransactionList from "../../components/TransactionList";

function Home() {
  return (
    <>
      <Header />
      <TransactionList />
    </>
  );
}

export default Home;
Enter fullscreen mode Exit fullscreen mode

E App.tsx:

import Home from "./pages/Home";
import GlobalStyle from "./styles/global";

function App() {
  return (
    <>
      <GlobalStyle />
      <Home />
    </>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

💄 Deixando essa página um pouco mais bonita

Estilização é algo que é muito difícil de aprender em tutorial. É algo que se aprende de tanto fazer e copiar grandes designs. Se você trabalha com web já deve entender pelo menos um pouco sobre Flexbox e/ou Grid Layout, mas se esse não é o seu caso aconselho a buscar entender um pouco sobre esses modelos de layout depois e por enquanto só digitar o que eu vou mostrar aqui de estilização já que o foco desses posts é mostrar o React e não CSS.

Para você que já entende muito bem de CSS, aqui vamos utilizar o básico e praticamente nada de avançado como Media Queries e responsividade, vamos ser o mais básico possível.

Caso não queira copiar todo o código daqui pra frente, basta clonar o projeto apenas com a parte da estilização pronta:

git clone -b entrypoint https://github.com/brunolipe-a/go-finances.git
Enter fullscreen mode Exit fullscreen mode

Mas eu aconselho a você digitar todo o código para fixar.

A estilização do Header fica assim:

//components/Header/styles.ts

import styled from 'styled-components'

export const Container = styled.div`
  background: #5636d3;
  padding: 30px 0;

  header {
    width: 1120px;
    margin: 0 auto;
    padding: 0 20px 150px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    color: #fff;

    span {
      font-size: 1.75rem;
      font-weight: medium;
    }

    nav {
      a {
        color: #fff;
        text-decoration: none;
        font-size: 16px;
        transition: opacity 0.2s;
        padding-bottom: 6px;
        border-bottom: 2px solid #FF872C;

        & + a {
          margin-left: 32px;
          margin-right: 5px;
        }

        &:hover {
          opacity: 0.6;
        }
      }
    }
  }
`
Enter fullscreen mode Exit fullscreen mode

Crie um arquivo em src/utils/formatValue.ts:

const formatValue = (value: number): string =>
  Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(
    value,
  );

export default formatValue;
Enter fullscreen mode Exit fullscreen mode

Novo componente Cards:

//components/Cards/styles.ts

import styled from 'styled-components';

interface CardProps {
  total?: boolean;
}

export const CardContainer = styled.section`
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: 32px;
  margin-top: -150px;
`;

export const Card = styled.div`
  background: ${({ total }: CardProps): string => (total ? '#FF872C' : '#fff')};
  padding: 22px 32px;
  border-radius: 5px;
  color: ${({ total }: CardProps): string => (total ? '#fff' : '#363F5F')};

  header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-weight: 500;
    a {
      background: #7159c1;
    }
    p {
      font-size: 16px;
    }
  }
  h1 {
    margin-top: 14px;
    font-size: 36px;
    font-weight: 500;
    line-height: 54px;
  }
`;
Enter fullscreen mode Exit fullscreen mode
//components/Cards/index.tsx

import formatValue from "../../utils/formatValue";
import { CardContainer, Card } from "./styles";

export interface Balance {
  income: number;
  outcome: number;
  total: number;
}

type CardsProps = {
  balance: Balance;
};

function Cards({ balance }: CardsProps) {
  return (
    <CardContainer>
      <Card>
        <header>
          <p>Entradas</p>
        </header>
        <h1>{formatValue(balance.income)}</h1>
      </Card>
      <Card>
        <header>
          <p>Saídas</p>
        </header>
        <h1>{formatValue(balance.outcome)}</h1>
      </Card>
      <Card total>
        <header>
          <p>Total</p>
        </header>
        <h1>{formatValue(balance.total)}</h1>
      </Card>
    </CardContainer>
  );
}

export default Cards;
Enter fullscreen mode Exit fullscreen mode

Temos que remover o antigo componente TransactionList e criar novamente utilizando uma pasta:

// components/TransactionList/styles.ts

import styled from 'styled-components';

export const TableContainer = styled.section`
  margin-top: 64px;
  table {
    width: 100%;
    border-spacing: 0 8px;
    th {
      color: #969cb3;
      font-weight: normal;
      padding: 20px 32px;
      text-align: left;
      font-size: 16px;
      line-height: 24px;
    }
    td {
      padding: 20px 32px;
      border: 0;
      background: #fff;
      font-size: 16px;
      font-weight: normal;
      color: #969cb3;
      &.title {
        color: #363f5f;
      }
      &.income {
        color: #12a454;
      }
      &.outcome {
        color: #e83f5b;
      }
    }
    td:first-child {
      border-radius: 8px 0 0 8px;
    }
    td:last-child {
      border-radius: 0 8px 8px 0;
    }
  }
`;
Enter fullscreen mode Exit fullscreen mode
// components/TransactionList/index.tsx

import formatValue from "../../utils/formatValue";

import { TableContainer } from "./styles";

export interface Transaction {
  id: string;
  title: string;
  value: number;
  type: "income" | "outcome";
  category: { title: string };
  created_at: string;
}

type TransactionListProps = {
  transactions: Transaction[];
};

function TransactionList({ transactions }: TransactionListProps) {
  return (
    <TableContainer>
      <table>
        <thead>
          <tr>
            <th>Título</th>
            <th>Preço</th>
            <th>Categoria</th>
            <th>Data</th>
          </tr>
        </thead>

        <tbody>
          {transactions.map((transaction) => (
              <tr key={transaction.id}>
                <td className="title">{transaction.title}</td>
                <td className={transaction.type}>
                  {formatValue(transaction.value)}
                </td>
                <td>{transaction.category?.title}</td>
                <td>
                  {new Date(transaction.created_at).toLocaleDateString("pt-br")}
                </td>
              </tr>
            ))}
        </tbody>
      </table>
    </TableContainer>
  );
}

export default TransactionList;
Enter fullscreen mode Exit fullscreen mode

E no src/pages/Home:

// pages/Home/styles.ts

import styled from 'styled-components';

export const Container = styled.div`
  width: 100%;
  max-width: 1120px;
  margin: 0 auto;
  padding: 40px 20px;
`;
Enter fullscreen mode Exit fullscreen mode
// pages/Home/index.tsx

import { useState } from "react";

import Header from "../../components/Header";

import Cards, { Balance } from "../../components/Cards";
import TransactionList, { Transaction } from "../../components/TransactionList";

import { Container } from "./styles";

function Home() {
  const [balance, setBalance] = useState<Balance>({
    income: 12.12,
    outcome: 6,
    total: 6.12,
  });

  const [transactions, setTransactions] = useState<Transaction[]>([
    {
      id: "1",
      category: { title: "Trabalho" },
      title: "Venda",
      type: "income",
      value: 12.12,
      created_at: "2021-02-03T00:00:00",
    },
    {
      id: "2",
      category: { title: "Comida" },
      title: "Lanche",
      type: "outcome",
      value: 6,
      created_at: "2021-02-21T00:00:00",
    },
  ]);

  return (
    <>
      <Header />
      <Container>
        <Cards balance={balance} />
        <TransactionList transactions={transactions} />
      </Container>
    </>
  );
}

export default Home;
Enter fullscreen mode Exit fullscreen mode

Na Home temos algo novo: useState<Transaction[]>.
Utilizamos <> para informar para o useState qual a tipagem do valor a ser armazenado. Isso ajuda ao desenvolver pois o próprio VSCode vai informar se tentarmos salvar um valor que poderá quebrar código.
Por exemplo, se fossemos salvar uma string seria: useState<string>. Vale lembrar que o valor inicial do estado deve ser do mesmo tipo da tipagem.

😄 Ficou bonito?

Aqui terminamos a estilização da tela de listagem. Aconselho a digitarem todo o código para guardar na mente como criar componentes estilizados.

No próximo post vamos criar uma API de teste utilizando apenas JSON e começar a consumir essa API para gerar nossa tela dinamicamente.

PS: Um agradecimento especial💜 ao pessoal da Rocketseat🚀 pelo conteúdo de qualidade em React.

Top comments (0)