Este post foi escrito em outubro de 2023, alguns dados fornecidos aqui podem ter sido alterados.
Projeto pessoal é uma maravilha, você é livre para escolher toda a stack da aplicação: database, ambientes, deploy, testes, back end, front end, como organizar as tasks e etc. Nesses projetos você consegue ter um nível de aprendizado que dificilmente teria em uma empresa. Então aproveite, "brinque" de startup nas horas vagas e arque com suas decisões.
Meu foco é o front end mas navego um pouco no back end porque é uma sensação ótima conseguir, sozinho, tirar uma ideia maluca do papel e colocá-la em prática.
Sou um cara das antigas (+40) e que já trabalhava com desenvolvimento antes do início do Jquery e Prototype. Nunca fiz faculdade na área, mas sou formado em faculdade pública de Música e Belas Artes... pois é.
Enfim, só pra dizer que já vi muita coisa por aí. Conceitos e ideias que vão em e vem, problemas antigos com novos nomes e soluções velhas com nova roupagem.
Mas o que quero focar aqui é sobre estilos numa aplicação WEB.
Nos últimos anos, trabalhando como desenvolvedor sênior em empresas de vários tamanhos, cada vez mais fui me afastando do CSS que, modéstias a parte, conheço muito bem e sempre achei divertido e terapêutico. Nunca reclamei disso, é mais que comum utilizar algum Design System já projetado e você "só" vai programando os blocos (às vezes aplicando Atomic Design, que já renderia outro artigo, quando faz sentido) e sem se preocupar muito com o estilo dos componentes.
Quando precisa ajustar algo, geralmente o projeto utiliza algum CSS-in-JS como Styled Components, Emotion, Stitches, Linaria e etc. Eles são ótimos, usei por muito tempo e adoro usá-los quando faz sentido.
Certa vez eu utilizei Sass com CSS Modules para estilizar um teste prático de front end para uma vaga numa empresa de médio porte e um dos "programadores sêniors" me perguntou na entrevista:
Qual o motivo de usar Sass? É algo incomum hoje em dia. Por que você não escolheu CSS in JS? Não acha uma má prática voltar a usar CSS ou Sass?
Apenas sorri. Mas, ao longo do tempo, vi cada vez mais programadores front-end que não sabiam quase nada de CSS, Modules, Custom Properties, o poder do efeito em cascata e suas inúmeras possibilidades. Profissionais que já entram no mercado apenas utilizando JS/TS como padrão para escrever os estilos e veem CSS como algo obsoleto, fora de moda. Que loucura!
Voltando ao meu caso. Nesse projeto zero em folha, decidi usar Server Components junto com o NextJs e dar uma chance ao tão comentado TailWind Css. É um projeto relativamente complexo, com grande foco na experiência do usuário, com interface simples mas com detalhes de interações diferenciadas e múltiplos temas.
Posto isso, eis minhas conclusões após 4 meses de uso diário.
O que é o Tailwind CSS?
Em resumo, para quem caiu aqui de paraquedas, o Tailwind CSS é um framework de CSS como o antigo Bootstrap.
A diferença principal é que cada classe no Tailwind equivale a uma propriedade do CSS (ou pseudo classe/elemento) mas com o nome ligeiramente diferente. O chamado Utility-first.
Por exemplo:
- A classe
z-0
resulta na propriedadez-index: 0;
. - A classe
overflow-hidden
resulta na propriedadeoverflow: hidden;
. - A classe
hover:underline
resulta na pseudo classe:hover { text-decoration: underline; }
Estética controversa
Nos exemplos que lemos por aí, inclusive na própria documentação, encontramos este tipo de estrutura de marcação:
<div class="w-9 h-9 rounded-lg flex items-center justify-center text-slate-700 peer-checked:font-semibold peer-checked:bg-slate-900 peer-checked:text-white">
Eu ligo muito para estética funcional do código (beleza + facilidade de ler), clean code com bom senso e organização.
Ao ler o código acima, isso me causa incômodo. Acho difícil compreender rapidamente o que está acontecendo e perco o foco com todas essas classes que estão poluindo minha marcação.
Na minha opinião, marcação e estilo devem ser isolados o máximo possível. Eu não preciso saber que um elemento tem posição flex
se não quero saber disso naquele momento.
Meu pensamento foi:
Tudo bem, são apenas exemplos, dá pra contornar. Vou criar uma função para lidar com isso.
E resolvi assim:
Cada componente, que continha algo estilizado, eu criei um styles.js
.
import { cleanClasses } from '@helpers'
export default function classNames({ hasSomeProp }) {
return {
content: cleanClasses(`
${hasSomeProp ? 'border-green' : 'border-black'}
border
flex
h-9
items-center
justify-center
peer-checked:bg-slate-900
peer-checked:font-semibold
peer-checked:text-white
rounded-lg
text-slate-700
w-9
`),
}
}
obs.: Sempre ordeno minhas propriedades em ordem alfabética.
E, dentro do meu componente, importei esse arquivo e apliquei ao elemento.
import classNames from './styles'
export default function MyComponent({ children, hasSomeProp }) {
const c = classNames({ hasSomeProp } )
return <div className={c.content}>{children}</div>
}
Resolvido, pensei eu!
Isolo todas as classes e deixo meu componente limpo! 🤘
Mas repare que no styles.js
tem uma função chamada cleanClasses()
. Eu tive que criá-la por conta da identação usando o template literals que estava zoando a minificação final do html.
Para resolver, e manter minha identação do jeito que acho que faz sentido, criei uma função para remover caracteres desnecessários que atrapalham a minificação no final do build.
import { isString } from '@helpers'
export default function cleanClasses(value) {
if (!isString(value)) return ''
const removeFalsyValues = value.replace(
/\b(?:undefined|null|false|NaN|'')\b/g,
'',
)
const removeExtraSpaces = removeFalsyValues.replace(/\s+/g, ' ')
const cleanedClasses = removeExtraSpaces.trim()
return cleanedClasses
}
Gostei da solução e me atendeu bem, mas já comecei a ficar com uma pulga atrás da orelha...
Eu gosto de programar mas sou preguiçoso, quanto menos eu programo mais eu gosto de programar. 😂
Abstrações desnecessárias
A documentação do Tailwind começou a ser parte do meu dia a dia de trabalho. Sempre havia uma aba aberta para eu consultar e ter certeza de que tal classe era a propriedade que eu queria.
E é normal, eu tenho que aprender o framework. Mas essa dependência em decorar classes, que são nomeações das propriedades (que eu já sabia de cór no CSS), começou a me irritar e achar as abstrações desnecessárias.
De certa forma, me atrevo a dizer, estava "desdecorando" as propriedades de CSS puro.
Usei esse termo de propósito pois depois de poucos meses usando TW, e já migrado o projeto para CSS Modules, eu me vi adicionando classes do Tailwind ao invés das propriedades de CSS. Como são nomes parecidos, meu cérebro simplesmente acabou substituindo esses valores.
Por exemplo, você sabe a qual propriedade a classe do Tailwind justify-center
se refere?
- 🔘
justify-content: center;
- 🔘
justify-items: center;
- 🔘
justify-self: center;
- 🔘
justify-tracks: center;
Dica, justify-tracks
ainda é experimental e nem existe no Tailwind.
Se você ficou na dúvida, vai ter que ir na documentação. 🤡
Lidando com seletores mais complexos
Um aspecto que não achei produtivo foi utilizar seletores mais complexos. Enquanto você estiliza apenas um único elemento é OK, mas quando precisa de algo mais complexo e que envolve outros elementos a coisa fica esquisita.
Por exemplo, digamos que eu precise modificar os itens, que não tenho acesso e não posso modificá-los, de uma lista.
obs.: Isso está longe de ser complexo.
<ul className={styles.ul}>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
Com Tailwind eu teria que adicionar algo como:
import { cleanClasses } from '@helpers'
export default function classNames() {
return {
ul: cleanClasses(`
[&_li]:rounded-full
[&_li]:border
[&_li]:border-black
[&_li]:bg-white
[&_li]:px-2
[&_li]:py-0.5
`),
}
}
É muito verboso dizer que cada propriedade é um item da lista com [&_li]
e ter que informar isso, repetidas vezes, em cada classe do TW. Sem falar que não consigo usar shorthand properties. Tem plugin pra isso, mas não quero entupir minha aplicação com pacotes.
Com CSS eu apenas declaro como:
.ul li {
background: var(--color__white);
border-radius: var(--radius__full);
border: 1px solid var(--color__black);
padding: var(--size__0-5) var(--size__2);
}
E nem vou entrar em detalhe de sibling selectors e outros seletores que são triviais com CSS nativo. Sinto-me reinventando a roda mas essa roda não é lá muito redonda. 😂
Temas
Outra coisa que não gostei foi a forma de adicionar múltiplos temas e seus diversos design tokens usando o TW. Pensei que ele lidaria com isso de forma nativa, mas não.
Uma possibilidade é instalar mais uma dependência como a tw-colors, já que não temos isso nativamente no framework (só podemos alterar os tokens do tema atual). Ela é muito boa e funciona bem.
Outra possibilidade é usar as variáveis de CSS nativas e informar ao Tailwind como usá-las... mas pera aí, eu não quero isso já que escolhi o framework para lidar com os estilos. Quero apenas usar o Tailwind e sua turminha.
Cadê o efeito em cascata?
Rapidamente aprendi que o TW remove uma das coisas mais importantes do CSS (que, inclusive, é o significado da primeira letra do seu acrônimo: Cascade Style Sheets.
obs.: A versão usada foi a 3.3.3
, provavelmente vão resolver isso no futuro.
Por exemplo, há situações em que você precisa sobrescrever algum estilo para customizar componentes já existentes.
Vamos supor que você tenha um componente que é usado em vários lugares, mas, num único lugar específico, você precisa que ele tenha algo diferente e só precisa sobrescrever uma propriedade de CSS. Adicionar uma nova prop ao componente, que só vai ser usada ali, não seria uma boa ideia.
No código abaixo, em função do efeito em cascata, espera-se que o valor white
seja substituído pelo black
na propriedade background
do ChildComponent
. Porque quando chamamos esse componente adicionamos um outro background que deve sobrescrever o default.
Ou seja, tudo o que está declarado depois e com a mesma especificidade, tem uma ordem de importância maior que a propriedade anterior.
A renderização da div
abaixo seria <div class="bgWhite bgBlack" />
import { ChildComponent } from './components'
import styles from './styles.module.css'
// .bgBlack { background: black; }
export default function ParentComponent() {
return (
<ChildComponent className={styles.bgBlack} />
)
}
import styles from './styles.module.css'
// .bgWhite { background: white; }
export default function ChildComponent({ className }) {
const bgStyles = [styles.white, className].join(' ')
return (
<div className={bgStyles}>
Some Text
</div>
)
}
Já no Tailwind, se a renderização do seu componente for <div class="bg-white bg-black" />
o bg-black
não necessariamente sobrescreverá o bg-white
, porque isso vai depender da ordem interna de classes do TW.
Ou seja, não há garantia que o efeito cascata seja aplicado! Poxa, mas é para isso que pago o CSS! 😅
Você consegue resolver isso, mas, adivinha, vai precisar acoplar mais uma dependência ao seu projeto usando o tailwind-merge que irá remover as classes indesejadas já que o efeito em cascata não funciona. Ou usar o !important. 😒
Decisão após análise
E, finalmente, como já foi dito, cheguei a conclusão que, para o meu caso, o ideal seria remover tudo relacionado ao Tailwind (tailwindcss
, postcss
, tw-colors
e tailwind-merge
) e usar apenas CSS Modules (já fornecido pelo NextJs como padrão).
Achei interessante a experiência de usar o Tailwind mas não me convenceu 100%. Tive que criar muitas soluções extras e instalações de pacotes para deixar minha experiência do jeito que acho que funciona.
Para projetos de prototipação, MVPs, ou até mesmo se a equipe seja apenas de devs back-end (que não querem aprender CSS), pode ser.
Mas se eu for criar um novo produto eu já descarto o uso do Tailwind. Ter um design tokens organizado com variáveis em CSS e usar CSS Modules é a escolha mais natural e eficiente pra mim. Nem de Sass eu iria. O básico bem feito é a tendência para os próximos anos, na verdade nunca deixou de ser.
Enfim, voltei a ter prazer em escrever e ler CSS.
Top comments (0)