Solução
Veja a diff no Github
Qual é o problema que estamos resolvendo?
Se você ainda não percebeu, nosso componente está flexível em praticamente todos aspectos de um botão html: todas os atributos esperados vêm tipados e prontos.
Entretanto, como estamos aqui usando Tailwind, será comum a alteração dos classNames
. Vamos tentar fazer isso e ver o que acontece. Eu quero que o primeiro botão fique com o fundo da cor rebeccapurple
(se não me engano é a última cor nomeada - named colors - adicionada no CSS).
Fazer isso alterando a prop style é uma maravilha e fácil:
Mas se fizermos isso usando tailwindCSS vai dar erro.
É claro, o className
no componente está hard-coded, e de duas uma:
- Se o spread do
{...props}
estiver antes doclassName
, nada vai acontecer - e nenhum estilo vai ser aplicado. - Se o spread do
{...props}
estiver depois doclassName
, tudo vai ser sobrescrito e o único estilo vai ser obackgroundColor
que você passou!
Uma solução não definitiva
a solução intuitiva (e que já usei várias vezes) é receber o classNames
como props e adicioná-lo à string de classNames usando template strings:
Isso pode funcionar em alguns casos (e funciona no caso da cor). Mas o método não é nada confiável.
Isso porque pode haver conflito de classes definidas dentro do componente com as classes que você passar quando instanciar/utilizar o componente.
Por exemplo, se eu passar um p-0
ao usar meu componente, esse estilo não vai funcionar já que dentro do componente já tem um px-4
e um py-2
e todas essas classes vão entrar em conflito.
A solução definitiva
”Se eu vi mais longe, foi por estar de pé sobre ombros de gigantes.”
Vamos procurar um “gigante” para subir no ombro dele e tentar encontrar a resposta?
O gigante aqui vai ser o shadcn
, que fez uma das mais populares bibliotecas de ui
de react com typescript.
Ele resolve esse problema fazendo uma função auxiliar que dá o nome de cn
. Vamos “copiá-la” também?
Para isso, vamos adicionar um arquivo utils
em lib/utils.ts
.
A clsx
é uma lib para juntar condicionalmente nomes de classes. É frequentemente utilizada em aplicações React para lidar com classes dinâmicas baseadas em diferentes condições.
A tailwind-merge
serve justamente para resolver conflitos em classes do TailwindCSS, permitindo um agrupamento mais eficiente de estilos (o caso do p-0
combinado com px-1
).
a função cn
que é exportada vai dar sempre prioridade para os últimos argumentos adicionados. Então podemos utilizá-lo no nosso <Button>
dessa forma:
Pronto! Agora sim nosso componente está verdadeiramente flexível!