Por que você não pode apagar CSS "não usado" buscando pelo nome da classe
Eu ia apagar um bloco de CSS que parecia morto. A busca dizia que várias classes não eram usadas em lugar nenhum, e a busca estava errada.
Eu fiz o MyIPScan, um conjunto de testes gratuitos de IP, DNS, WebRTC e vazamentos de VPN. Com o tempo, a folha de estilo cresceu e eu quis limpar o que parecia sobrar. O método óbvio: pegar cada classe do CSS, buscar no código e apagar o que não aparece. Não funcionou.
O que a busca não enxerga
Parte das classes do site não existe como texto em lugar nenhum do código. Elas são montadas em JavaScript a partir de um prefixo e de um slug:
// esboço ilustrativo, não é o código original
const key = route.slug; // por exemplo "dns"
el.className = `per-route-status-${key}`;
No CSS existe .per-route-status-dns. No código, só existe per-route-status- e uma variável. Se você buscar per-route-status-dns, encontra zero resultados. A classe parece morta, mas pode aparecer na tela de quem abre aquela rota.
O tamanho do problema
Contei o que havia no projeto:
- 486 nomes de classe estáticos, escritos por extenso;
- 70 prefixos que viram classes quando juntados com algo em tempo de execução;
- 2.506 slugs que podem completar esses prefixos.
Não dá para conferir isso no olho. E nenhuma ferramenta que só lê texto consegue saber quais combinações de prefixo e slug realmente acontecem.
Por que as ferramentas de "CSS não usado" erram aqui
Em geral, essas ferramentas funcionam de dois jeitos. Ou leem o código-fonte procurando o nome da classe, e aí caem no mesmo buraco da busca. Ou abrem páginas num navegador e anotam o que foi aplicado, e aí só veem as rotas e estados que você visitou. Um estado de erro que aparece uma vez por mês não está no relatório, e o CSS dele vai embora.
Nos dois casos, o resultado é o mesmo: "não usado" na verdade quer dizer "não encontrado por este método".
O que fazer antes de apagar
1. Liste os prefixos dinâmicos primeiro. Busque por template strings e concatenações que montam classes:
grep -rnoE '[A-Za-z0-9_-]+[-_]\$\{' . --include='*.js' --include='*.html'
grep -rnoE "[A-Za-z0-9_-]+[-_]['\"] *\+" . --include='*.js' --include='*.html'
O -o imprime só o prefixo, então a saída já vira uma lista. Toda classe do CSS que começa com um desses prefixos fica fora da lista de remoção até você provar o contrário. Atenção: nomes montados em duas etapas, como `${base}-${state}` ou ["status", key].join("-"), nenhum grep encontra. Esses você precisa achar lendo o código.
Se você usa PurgeCSS, transforme cada prefixo encontrado numa regra de safelist. O PurgeCSS extrai tokens do código-fonte; de `per-route-status-${key}` ele só vê per-route-status-, e a classe completa seria removida:
// purgecss.config.js
module.exports = {
content: ["**/*.html", "**/*.js"],
css: ["styles.css"],
safelist: {
standard: [/^per-route-status-/],
},
};
O Tailwind usa a mesma ideia com safelist e pattern.
2. Prefira uma lista explícita a montar o nome. Um mapa deixa o nome inteiro visível para a busca:
const STATUS_CLASS = {
dns: "per-route-status-dns",
webrtc: "per-route-status-webrtc",
};
el.className = STATUS_CLASS[key];
Fica mais verboso, mas agora a busca pelo nome encontra o uso. Para 2.506 slugs isso não é prático em todos os casos, então use onde a lista é curta e estável.
3. Apague em lote pequeno e confira na tela. Remova um grupo, faça o deploy num ambiente de preview e abra as rotas e estados que usam aquele prefixo. Se uma tela mudou, o CSS estava vivo.
A regra
Zero resultados na busca só mostram que o nome não está escrito por extenso. Antes de apagar CSS, descubra como os nomes de classe são montados no seu projeto.
O MyIPScan é gratuito e os testes estão em myipscan.net.