Construindo componentes reutilizáveis ​​do React usando o Tailwind

Publicados: 2022-03-10
Resumo rápido ↬ O Tailwind é um framework CSS popular e utilitário que fornece nomes de classes de baixo nível para desenvolvedores web. Ele não possui JavaScript e funciona bem com frameworks existentes, como React, Vue, Angular, Ember e outros. Embora isso seja positivo, pode ser confuso para novos desenvolvedores entender como integrar o Tailwind em seus aplicativos. Neste artigo, exploraremos maneiras de criar componentes React reutilizáveis ​​usando o Tailwind.

Neste post, veremos várias maneiras diferentes de criar componentes React reutilizáveis ​​que aproveitam o Tailwind sob o capô enquanto expõem uma interface agradável para outros componentes. Isso melhorará seu código, passando de longas listas de nomes de classes para props semânticos que são mais fáceis de ler e manter.

Você precisará ter trabalhado com React para entender bem este post.

Tailwind é uma estrutura CSS muito popular que fornece classes de utilitários de baixo nível para ajudar os desenvolvedores a criar designs personalizados. Ele cresceu em popularidade nos últimos anos porque resolve dois problemas muito bem:

  1. O Tailwind facilita fazer alterações iterativas no HTML sem vasculhar as folhas de estilo para encontrar seletores CSS correspondentes.
  2. Tailwind tem convenções e padrões sensatos. Isso torna mais fácil para as pessoas começarem sem escrever CSS do zero.

Adicione a documentação abrangente e não é nenhuma surpresa por que o Tailwind é tão popular.

Esses métodos ajudarão você a transformar o código que se parece com isso:

 <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"> Enable </button>

Para codificar assim:

 <Button size="sm" textColor="white" bgColor="blue-500"> Enable </Button>

A diferença entre os dois snippets é que no primeiro usamos uma tag de botão HTML padrão, enquanto no segundo usamos um componente <Button> . O componente <Button> foi construído para reutilização e é mais fácil de ler, pois possui uma semântica melhor. Em vez de uma longa lista de nomes de classes, ele usa propriedades para definir vários atributos, como size , textColor e bgColor .

Vamos começar.

Mais depois do salto! Continue lendo abaixo ↓

Método 1: Controlando classes com o módulo Classnames

Uma maneira simples de adaptar o Tailwind em um aplicativo React é adotar os nomes das classes e alterná-los programaticamente.

O módulo classnames npm facilita a alternância de classes no React. Para demonstrar como você pode usar isso, vamos pegar um caso de uso onde você tem componentes <Button> em seu aplicativo React.

 // This could be hard to read. <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Enable</button> // This is more conventional React. <Button size="sm" textColor="white" bgColor="blue-500">Enable</Button>

Vamos ver como separar as classes do Tailwind para que as pessoas que usam este componente <Button> possam usar props React como size , textColor e bgColor .

  1. Passe adereços como bgColor e textColor diretamente no modelo de string de nome de classe.
  2. Use objetos para alternar nomes de classes programaticamente (como fizemos com a prop size )

No código de exemplo abaixo, veremos as duas abordagens.

 // Button.jsx import classnames from 'classnames'; function Button ({size, bgColor, textColor, children}) { return ( <button className={classnames("bg-${bgColor} text-${textColor} font-bold py-2 px-4 rounded", { "text-xs": size === 'sm' "text-xl": size === 'lg', })}> {children} </button> ) }; export default Button;

No código acima, definimos um componente Button que recebe as seguintes props:

  • size
    Define o tamanho do botão e aplica as classes Tailwind text-xs ou text-xl
  • bgColor
    Define a cor de fundo do botão e aplica as classes Tailwind bg-* .
  • textColor
    Define a cor do texto do botão e aplica as text-* classes Tailwind .
  • children
    Quaisquer subcomponentes serão passados ​​por aqui. Geralmente conterá o texto dentro do <Button> .

Ao definir Button.jsx , agora podemos importá-lo e usar props React em vez de nomes de classe. Isso torna nosso código mais fácil de ler e reutilizar.

 import Button from './Button'; <Button size="sm" textColor="white" bgColor="blue-500">Enable</Button>

Usando nomes de classe para componentes interativos

Um Button é um caso de uso muito simples. Que tal algo mais complicado? Bem, você pode levar isso adiante para criar componentes interativos.

Por exemplo, vamos ver uma lista suspensa que é feita usando o Tailwind.


Uma lista suspensa interativa criada usando Tailwind e alternância de nome de classe.

Para este exemplo, criamos o componente HTML usando nomes de classe CSS Tailwind, mas expomos um componente React que se parece com isso:

 <Dropdown options={\["Edit", "Duplicate", "Archive", "Move", "Delete"\]} onOptionSelect={(option) => { console.log("Selected Option", option)} } />

Observando o código acima, você notará que não temos nenhuma classe Tailwind. Eles estão todos ocultos dentro do código de implementação de <Dropdown/> . O usuário deste componente Dropdown só precisa fornecer uma lista de options e um manipulador de cliques, onOptionSelect quando uma option é clicada.

Vamos ver como esse componente pode ser construído usando o Tailwind.

Removendo parte do código não relacionado, aqui está o cerne da lógica. Você pode ver este Codepen para um exemplo completo.

 import classNames from 'classnames'; function Dropdown({ options, onOptionSelect }) { // Keep track of whether the dropdown is open or not. const [isActive, setActive] = useState(false); const buttonClasses = `inline-flex justify-center w-full rounded-md border border-gray-300 px-4 py-2 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-blue-500 active:text-gray-200 transition ease-in-out duration-150`; return ( // Toggle the dropdown if the button is clicked <button onClick={() => setActive(!isActive)} className={buttonClasses}> Options </button> // Use the classnames module to toggle the Tailwind .block and .hidden classes <div class={classNames("origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg", { block: isActive, hidden: !isActive })}> // List items are rendered here. {options.map((option) => <div key={option} onClick={(e) => onOptionSelect(option)}>{option}</div>)} </div> ) } export default Dropdown;

A lista suspensa se torna interativa mostrando ou ocultando-a seletivamente usando as classes .hidden e .block . Sempre que o <button> é pressionado, acionamos o manipulador onClick que alterna o estado isActive . Se o botão estiver ativo ( isActive === true ), definimos a classe do block . Caso contrário, definimos a classe hidden . Ambas são classes Tailwind para alternar o comportamento de exibição.

Em resumo, o módulo classnames é uma maneira simples e eficaz de controlar programaticamente nomes de classe para o Tailwind. Torna mais fácil separar a lógica em props React, o que facilita a reutilização de seus componentes. Funciona para componentes simples e interativos.

Método 2: Usando constantes para definir um sistema de design

Outra maneira de usar o Tailwind e o React juntos é usando constantes e adereços de mapeamento para uma constante específica. Isso é eficaz para sistemas de design de edifícios. Vamos demonstrar com um exemplo.

Comece com um arquivo theme.js onde você lista seu sistema de design.

 // theme.js (you can call it whatever you want) export const ButtonType = { primary: "bg-blue-500 hover:bg-blue-700 text-white font-bold rounded", secondary: "bg-blue-500 hover:bg-blue-700 text-white font-bold rounded", basic: "bg-white hover:bg-gray-700 text-gray-700 font-bold rounded", delete: "bg-red-300 hover:bg-red-500 text-white font-bold rounded" }; export const ButtonSize = { sm: "py-2 px-4 text-xs", lg: "py-3 px-6 text-lg" }

Neste caso, temos dois conjuntos de constantes:

  • ButtonType define como os botões são estilizados em nosso aplicativo.
  • ButtonSizes define os tamanhos dos botões em nosso aplicativo.

Agora, vamos escrever nosso componente <Button> :

 import {ButtonType, ButtonSize} from './theme'; function Button({size, type, children}) { // This can be improved. I'm keeping it simple here by joining two strings. const classNames = ButtonType[type] + " " + ButtonSize[size]; return ( <button className={classNames}>{children}</button> ) } export default Button;

Usamos as constantes ButtonType e ButtonSize para criar uma lista de nomes de classes. Isso torna a interface do nosso <Button> muito mais agradável. Ele nos permite usar props de size e type em vez de colocar tudo em uma string de nome de classe.

 // Cleaner and well defined props. <Button size="xs" type="primary">Enable</Button>

Contra a abordagem anterior:

 // Exposing class names <button className="py-2 px-4 text-xs bg-blue-500 hover:bg-blue-700 text-white font-bold rounded">Enable</button>

Se você precisar redefinir a aparência dos botões em seu aplicativo, basta editar o arquivo theme.js e todos os botões em seu aplicativo serão atualizados automaticamente. Isso pode ser mais fácil do que pesquisar nomes de classes em vários componentes.

Método 3: Compondo Utilitários com @apply

Uma terceira maneira de melhorar a legibilidade de seus componentes React é usar CSS e o padrão @apply disponível no PostCSS para extrair classes repetidas. Esse padrão envolve o uso de folhas de estilo e pós-processadores.

Vamos demonstrar como isso funciona através de um exemplo. Suponha que você tenha um grupo de botões que tenha um botão primário e um secundário.

Um grupo de botões que consiste em um botão primário e secundário
Um grupo de botões que consiste em um botão primário e secundário. (Visualização grande)
 <button className="py-2 px-4 mr-4 text-xs bg-blue-500 hover:bg-blue-700 text-white font-bold rounded">Update Now</button> <button className="py-2 px-4 text-xs mr-4 hover:bg-gray-100 text-gray-700 border-gray-300 border font-bold rounded">Later</button>

Usando o padrão @apply , você pode escrever este HTML como:

 <button className="btn btn-primary btn-xs">Update Now</button> <button className="btn btn-secondary btn-xs">Later</button>

Que pode então ser adotado para React para se tornar:

 import classnames from "classnames"; function Button ({size, type, children}) { const bSize = "btn-" + size; const bType = "btn-" + type; return ( <button className={classnames("btn", bSize, bType)}>{children}</button> ) } Button.propTypes = { size: PropTypes.oneOf(['xs, xl']), type: PropTypes.oneOf(['primary', 'secondary']) }; // Using the Button component. <Button type="primary" size="xs">Update Now</Button> <Button type="secondary" size="xs">Later</Button>

Veja como você criaria esses nomes de classe no estilo BEM, como .btn , .btn-primary e outros. Comece criando um arquivo button.css :

 /\* button.css \*/ @tailwind base; @tailwind components; .btn { @apply py-2 px-4 mr-4 font-bold rounded; } .btn-primary { @apply bg-blue-500 hover:bg-blue-700 text-white; } .btn-secondary { @apply hover:bg-gray-700 text-gray-700 border-gray-300 border; } .btn-xs { @apply text-xs; } .btn-xl { @apply text-xl; } @tailwind utilities;

O código acima não é CSS real , mas será compilado pelo PostCSS. Há um repositório GitHub disponível aqui que mostra como configurar PostCSS e Tailwind para um projeto JavaScript.

Há também um pequeno vídeo que demonstra como configurá-lo aqui.

Desvantagens de usar @apply

O conceito de extrair classes de utilitário Tailwind em classes CSS de nível superior parece fazer sentido, mas tem algumas desvantagens que você deve estar ciente. Vamos destacá-los com outro exemplo.

Primeiro, ao extrair esses nomes de classes, perdemos algumas informações. Por exemplo, precisamos estar cientes de que .btn-primary deve ser adicionado a um componente que já tenha .btn aplicado a ele. Além disso, .btn-primary e .btn-secondary não podem ser aplicados juntos. Esta informação não é evidente apenas olhando para as classes.

Se esse componente fosse algo mais complicado, você também precisaria entender a relação pai-filho entre as classes. De certa forma, esse é o problema que o Tailwind foi projetado para resolver e, usando @apply , estamos trazendo os problemas de volta, de uma maneira diferente.

Aqui está um vídeo em que Adam Wathan - o criador do Tailwind - mergulha nos prós e contras de usar @apply .

Resumo

Neste artigo, examinamos três maneiras de integrar o Tailwind em um aplicativo React para criar componentes reutilizáveis. Esses métodos ajudam você a construir componentes React que possuem uma interface mais limpa usando props .

  1. Use o módulo classnames para alternar as classes programaticamente.
  2. Defina um arquivo de constantes onde você define uma lista de classes por estado de componente.
  3. Use @apply para extrair classes CSS de nível superior.

Se você tiver alguma dúvida, envie-me uma mensagem no Twitter em @tilomitra.

Leitura recomendada no SmashingMag:

  • Configurando Tailwind CSS em um projeto React
  • Criando tabelas classificáveis ​​com React
  • Um guia para CSS DevTools novo e experimental no Firefox
  • Faça seus próprios painéis de conteúdo de expansão e contratação