Como estilizar componentes de bibliotecas externas com CSS

Saiba como utilizar tipos de seletores para modificar manualmente cores e comportamentos específicos de um componente

*Texto de Gean Lucas Freire
Frontend Software Engineer na Compass UOL e membro do Space Squad da Rocketseat

Pré-requisitos

Durante o post, abordaremos um exemplo prático de estilização. Para melhor experiência na leitura do post, você precisa entender:

  • CSS, JavaScript, TypeScript, React, Styled Components

Antes de seguir com o texto, se você quiser saber como explorar as possibilidades do CSS, dá uma olhada nesse vídeo:

Introdução

Durante o desenvolvimento de aplicações web, frequentemente surge a necessidade de criarmos um componente estilizado com base no design proposto pela nossa equipe. No entanto, alguns componentes podem ser muito trabalhosos de serem criados manualmente, e as versões disponíveis nos repositórios nem sempre aceitam modificações tão facilmente quanto um componente que foi criado do zero. Nesses casos, podemos utilizar algumas ferramentas do CSS, como seletores, que nos possibilitam escolher exatamente qual parte do componente queremos alterar, facilitando nosso desenvolvimento.

Configurando o projeto

Neste artigo, vamos analisar um componente de Range Calendar, da biblioteca React Spectrum, mas o exemplo pode ser aplicado em qualquer componente com base nos seletores que vamos utilizar. Além disso, vamos utilizar a biblioteca Styled Components para auxiliar nos componentes CSS e o Vite para iniciar o projeto.

Primeiro passo é iniciar o projeto com Vite:

yarn create vite

Aqui você pode criar o projeto com qualquer nome. No meu caso, criei como “web”. Na parte de escolha de framework, escolhi “React”; e na parte de selecionar variante, “TypeScript”.

O próximo passo é instalar a Styled Components e o Vite

yarn add @adobe/react-spectrum
yarn add styled-components
yarn add -D @types/styled-components

Para facilitar o processo, você pode acessar os arquivos iniciais do projeto por aqui, e se basear por lá, removendo os arquivos que não serão utilizados.

Por enquanto nosso componente app está assim:

function App() {
  return <h1>Hello CSS!</h1>;
}

export default App;

Para usar a React Spectrum, especificamente, vamos precisar adicionar o componente de Provider na raiz dos nossos componentes. Para isso, vamos adicionar o defaultTheme, deixando o app assim:

import { defaultTheme, Provider } from "@adobe/react-spectrum";

function App() {
  return (
    <Provider theme={defaultTheme} colorScheme="light">
      <h1>Hello CSS!</h1>
    </Provider>
  );
}

export default App;

Criando o nosso componente

Vamos criar agora um componente de Range Picker, onde o usuário poderá escolher o intervalo de datas. Para isso, vou criar um componente em src/components chamado DateRangeCalendar. Utilizaremos Styled Components para criar esse componente, então criaremos um arquivo styles.ts separado do index.tsx, dessa forma, poderemos mexer diretamente nos estilos.

Agora que importamos o nosso componente no app, não vamos mais mexer por lá.

import { defaultTheme, Provider } from "@adobe/react-spectrum";
import { DateRangeCalendar } from "./components/DateRangeCalendar";

function App() {
  return (
    <Provider theme={defaultTheme} colorScheme="light">
      <DateRangeCalendar></DateRangeCalendar>
    </Provider>
  );
}

export default App;
export default App;

import { Container } from "./styles";

export function DateRangeCalendar() {
  return <Container>Hello CSS!</Container>;
}
import styled from "styled-components";

export const Container = styled.div``;

Para conferir a estrutura dos arquivos até agora, você pode acessar esse commit.

Nosso próximo passo é importar o Range Calendar da React Spectrum. Como é um componente de calendário, ele precisa fazer algumas verificações para mostrar as datas corretamente.

Para isso, vamos instalar a biblioteca Internationalized:

yarn add @internationalized/date

E utilizaremos um estado simples para salvar a seleção de data do usuário, junto com a função parseAbsolute da internationalized para converter uma data específica:

import { RangeCalendar } from "@adobe/react-spectrum";
import { getLocalTimeZone, parseAbsolute } from "@internationalized/date";
import { useState } from "react";
import { Container } from "./styles";

export function DateRangeCalendar() {
  const [date, setDate] = useState({
    start: parseAbsolute(new Date().toISOString(), getLocalTimeZone()),
    end: parseAbsolute(new Date().toISOString(), getLocalTimeZone()),
  });

  return (
    <Container>
      <span>
        Data inicial: <span>{date.start.toString()}</span>
      </span>
      <span>
        Data final: <span>{date.end.toString()}</span>
      </span>
      <RangeCalendar value={date} onChange={setDate} visibleMonths={2} />
    </Container>
  );
}

export const Container = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2rem;
`;

Já podemos ver nosso componente, e ele mostra um novo range de data do tipo ISO toda vez que o usuário faz a seleção, veja o commit aqui


Vamos tentar mudar agora o background dos meses do nosso calendário. Se você for na página da biblioteca, vai ver que as cores são alteradas baseado no esquema de cores criado por eles e também  nos temas claro ou escuro do usuário. Portanto, se por algum motivo a gente precisasse colocar uma cor específica, não seria possível normalmente. Para exemplificar, vamos tentar mudar a cor de branco para roxo.

Se inspecionarmos os meses, podemos ver que cada um deles é renderizado como uma tabela na nossa árvore de elementos

Nesse caso, podemos acessar diretamente esse componente através do nosso Container, buscando estilizar diretamente todos os componentes de table que sejam filhos do Container

export const Container = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2rem;

  table {
    background: purple;
    border-radius: 20px;
  }
`;

E, com isso, nosso componente ficará assim:

No entanto, podemos ver que essa estilização dificulta a leitura dos dias, já que a cor do texto continuou igual. Continuando nossos testes, vamos tentar mudar para branco agora. O primeiro lugar que devemos alterar é a parte que marca os dias da semana. Se inspecionarmos, veremos que todos eles são <th> com um <span> que contém um dia específico.

Sabendo disso, basta adicionar mais uma estilização dentro do Container que chegue diretamente no <span> dentro da <th>.

export const Container = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2rem;

  table {
    background: purple;
    border-radius: 20px;
  }

  th {
    span {
      color: white;
    }
  }
`;


Poderíamos fazer a mesma coisa com as cores dos dias. No entanto, ao testar, você vai notar que as cores de todos os dias será alterada, e, desta maneira, não poderíamos diferenciar o dia inicial e o final da seleção, nem selecionar uma cor diferente para os dias da seleção. Então, vamos utilizar aqui os seletores de atributos do CSS para escolher exatamente a classe que queremos alterar.

Primeiro, vamos tentar alterar a cor dos dias da seleção. Ao inspecionarmos, podemos ver que todos eles possuem uma classe que começa com is-range-selection. Essa classe é responsável pelos dias dentro da seleção

Adicionando o seguinte código no nosso Container, veremos que apenas as cores de texto da seleção foram alteradas

[class*="is-range-selection"] {
    span {
      color: white;
    }
  }

Como o nome já diz, os seletores de atributos do CSS são responsáveis por selecionar componentes com base nos atributos que eles possuem. No nosso caso, estamos selecionando o componente e verificando todos que possuam uma classe que tenha is-range-selected em qualquer parte dela, através do * que passamos depois do nome do atributo que queremos verificar.

Se você lembrar do nosso componente inicial, verá que ele tinha uma cor diferente de texto e background para a data inicial e final da seleção. Podemos resolver isso também com os seletores

[class*="is-selection-start"],
  [class*="is-selection-end"] {
    span {
      color: cyan;
      background: orange;
      border-radius: 50%;
    }
  }

Com isso, podemos alterar a estilização de praticamente todos os elementos do nosso componente, basta olhar qual a classe correspondente. Você pode ver como ficou o código com essas alterações aqui. Lembrando que estes seletores estão no escopo do nosso Container, então se tiver mais algum componente que receba as mesmas classes, todas elas serão alteradas. Para resolver isso, basta colocar esses seletores dentro do CSS da nossa table, com isso você terá uma especificidade maior para alterar os estilos

Conclusão

O CSS oferece nativamente muitas opções para estilizar componentes, isso nos permite fazer praticamente qualquer tipo de alteração nos estilos de bibliotecas externas que não possuem uma fácil estilização com base no componente que queremos criar, se soubermos qual atributo e classe procurar. Com isso, casos de design que parecem complexos à primeira vista, podem ser facilmente resolvidos se conhecermos as técnicas certas através do CSS.

E aí, curtiu o conteúdo?

#NeverStopLearning

Feito com 💜 pelo SpaceSquad da Rocketseat.