@shadcn/lint é um linter open source de shadcn, para ESLint e Oxlint, que converte seu sistema de design em Tailwind CSS em erros que seu agente de IA lê e corrige sozinho. Cada erro explica o que quebrou e o que usar no lugar, a partir de seus próprios componentes, variantes e tema. O agente escreve p-[13px], executa npm run lint, recebe o tamanho correto e arruma seu próprio diff antes que um revisor humano veja.
É MIT, é publicado no npm como @shadcn/lint e funciona em qualquer projeto com Tailwind v4, quer você use shadcn/ui ou não.
O que é @shadcn/lint e o que faz um linter de sistema de design?
Um linter é uma ferramenta que analisa seu código sem executá-lo e reporta o que viola um conjunto de regras. É o mesmo que fazem ESLint ou Biome com erros de JavaScript. @shadcn/lint aplica essa ideia ao seu design system: não verifica se suas classes de Tailwind são válidas, mas como é permitido estilizar cada componente.
Você declara o que é permitido: que classes um Button aceita, se o padding deve ficar na escala de espaçamento, se cores soltas como bg-pink-500 são proibidas. O plugin reporta cada violação com uma correção tirada do seu próprio código.
shadcn o descreve como um agent-first linter. As regras são regras de lint normais, que rodam em seu editor e em sua CI como qualquer outra. O que muda é a mensagem de erro: está escrita para que um coding agent atue sobre ela, não para que um humano a leia de passagem.
Você precisa de shadcn/ui para usá-lo?
Não. O README diz que funciona com seus próprios componentes e seu próprio tema de Tailwind. Os projetos com shadcn/ui têm descobrimento automático de componentes e tema por meio de components.json. Se não usar shadcn/ui, você indica onde estão seus componentes com as configurações ui ou componentImports.
Por que um linter e não regras em AGENTS.md?
Porque um linter verifica o resultado, e seu erro diz o que fazer em seguida. O README ilustra isso com um Button que controla seu próprio padding. Você pode impor isso em TypeScript restringindo a prop style, mas o erro do compilador apenas diz que padding não é permitido. Não diz como dimensionar o botão.
Com @shadcn/lint, <Button className="p-4"> produz algo mais parecido com um comentário de revisão de código:
"p-4" is not allowed on <Button>: <Button> owns its spacing.
Use a size (sm, lg), or margin here or gap on the parent for space around it.
Add a size in components/ui/button.tsx only if the design explicitly calls for one.
As regras escritas em prosa, em AGENTS.md ou em um prompt de sistema, dependem de o agente lembrá-las e aplicá-las. Um comando de lint é uma verificação que o agente executa e que não pode contornar com argumentos.
Que regras inclui?
Traz seis regras:
| Regra | O que detecta |
|---|---|
no-restyle |
Alterar o estilo de um componente com className |
no-raw-colors |
Cores soltas como bg-pink-500 |
no-arbitrary-values |
Valores arbitrários como p-[13px] |
no-inline-styles |
Estilos inline e elementos <style> |
no-unknown-classes |
Classes que Tailwind não consegue gerar, como rounded-huge |
require-static-classes |
Classes que o linter não consegue ler, como `bg-${color}` |
no-restyle faz a maior parte do trabalho; as demais fecham as vias de escape.
Como se instala com ESLint ou Oxlint?
A via mais rápida é que seu próprio agente o instale. O quickstart do README é um prompt:
Read https://github.com/shadcn-ui/lint/blob/a89d04792f340bcea26af2d539a9eec7285fcc77/SETUP.md
and set up @shadcn/lint in this project.
SETUP.md indica ao agente que faça o seguinte:
- detectar seu gerenciador de pacotes;
- registrar o plugin no linter que você já usa, ou configurar Oxlint se não tiver nenhum;
- preservar suas regras existentes.
Também lhe indica expressamente que não ative nenhuma regra. Decidir o que é permitido fica por sua conta.
Se preferir fazer manualmente, você precisa de Node.js 20.19 ou superior.
ESLint (9.30 ou superior):
npm install -D @shadcn/lint eslint @typescript-eslint/parser
// eslint.config.mjs
import { plugin as shadcn } from "@shadcn/lint"
import tsParser from "@typescript-eslint/parser"
import { defineConfig } from "eslint/config"
export default defineConfig([
{
files: ["**/*.{js,jsx,ts,tsx}"],
languageOptions: {
parser: tsParser,
parserOptions: { ecmaFeatures: { jsx: true } },
},
plugins: { shadcn },
rules: {
"shadcn/no-arbitrary-values": "error",
},
},
])
Oxlint (1.80 ou superior; sua API de plugins em JS ainda está em alpha):
npm install -D @shadcn/lint oxlint
{
"jsPlugins": ["@shadcn/lint"],
"rules": {
"shadcn/no-arbitrary-values": "error"
}
}
Depois adicione o comando (eslint . ou oxlint) como script lint em package.json e coloque esta linha em AGENTS.md:
After making changes, run `npm run lint` and fix all errors.
Essa linha fecha o ciclo: sem ela, o agente não sabe que o linter existe.
Como escrever regras para seu sistema de design em Tailwind CSS?
Comece com três. Juntas cobrem a deriva mais comum: componentes reestilizados de fora, espaçamento fora de escala e cores fixas no código.
1. Deixe um Button se mover, mas não mudar de forma. Um contrato define que classes um componente concreto aceita:
"shadcn/no-restyle": ["error", {
allow: ["layout"],
contracts: [
{ pattern: "^Button$", allow: ["w-full", "mt-*", "mb-*"] },
],
}]
````<Button size="lg" className="mt-4 md:w-full" />` passa. `<Button className="p-4 hover:rounded-full" />` falha, e `<Button className="md:h-12 w-48" />` também.
**2. Permite alterar o espaçamento, mas apenas dentro da sua escala.** Combine um contrato que abre o espaçamento em `CardContent` com `no-arbitrary-values`:
```js
"shadcn/no-restyle": ["error", {
allow: ["layout"],
contracts: [
{ pattern: "^CardContent$", allow: ["layout", "spacing"] },
],
}],
"shadcn/no-arbitrary-values": "error",
p-6 md:p-8 passa. md:p-[13px] falha.
3. Escreva o erro com suas próprias palavras. As mensagens personalizadas aceitam placeholders, então o agente vê os tamanhos reais do seu componente:
"shadcn/no-restyle": ["error", {
allow: ["layout"],
message: {
spacing: "Use a {{component}} size: {{sizes}}.",
},
}]
Para um Button com tamanhos sm e lg, o erro se lê Use a Button size: sm, lg. Ative no-raw-colors ao lado e o agente irá buscar seus design tokens em vez de escrever bg-pink-500.
Os contratos são associados por nome de componente, então você pode aplicá-los a componentes de pacotes de terceiros sem fazer um fork nem envolvê-los.
Funciona de verdade com agentes de IA?
Segundo as avaliações do próprio shadcn, sim. São dados autorrelatados, publicados no repositório com metodologia e identificadores de cada execução, mas não reproduzidos de forma independente.
Em mais de 150 execuções de tarefas, quase todas chegaram a zero violações após uma única rodada de correção:
- Em oito tarefas “de tentação”, que pedem estilos fora do sistema (um botão rosa, 13 px de padding, “faça destacar”), Sonnet 5 teve uma média de cerca de 70 violações por execução antes do feedback. Depois chegou a zero em 40 de 40 tarefas.
- Nenhum modelo usou um comentário
eslint-disablenem um estilo inline para conseguir isso. A correção típica foi adicionar uma variante ao componente, por exemplovariant="brand"em vez de uma cor hexadecimal fixa.
A comparação de custos é o mais interessante. Em execuções de controle, os agentes receberam as mesmas regras como texto e foram solicitados a revisar seu próprio trabalho. Comparado a isso, corrigir com os diagnósticos do linter custou menos nos três modelos:
- Haiku 4.5: de 1,41 para 0,74 dólares
- Sonnet 5: de 3,57 para 2,47 dólares
- Opus 5: de 4,35 para 3,93 dólares
Quanto menor o modelo, maior o ganho.
A própria página de avaliações lista seus limites: todas as execuções usaram uma única família de modelos, e o juiz que pontuou a fidelidade visual é dessa mesma família.
@shadcn/lint ou eslint-plugin-tailwindcss?
Resolhem problemas distintos. eslint-plugin-tailwindcss impõe higiene de Tailwind: ordem de classes, abreviações, classes contraditórias e nomes de classe personalizados. Seu README indica que o suporte para Tailwind v4 é parcial e está disponível apenas em um canal beta. @shadcn/lint foi feito para Tailwind v4 e impõe seu sistema de design: qual componente aceita qual classe e o que usar em seu lugar. Se seu problema é a ordem inconsistente das classes, você precisa do primeiro. Se seu problema é um agente que restaura o estilo do seu Button em cada PR, você precisa do segundo.
Funciona com Vue e Svelte?
Sim, desde a versão 0.2.0 (publicada em 22 de setembro de 2026), com uma limitação. Com ESLint, lê os templates de .vue e .svelte através de vue-eslint-parser e svelte-eslint-parser. Com Oxlint, analisa apenas os blocos <script>, porque Oxlint ainda não consegue ler esses templates. No momento da publicação desta nota, o README principal do repositório descreve apenas a instalação para React; o pacote npm e a pasta docs/ já cobrem os três frameworks.
O que não consegue detectar?
O que fica fora da marcação dos seus componentes. No teste de red-team do shadcn, foram tentadas sete formas de contornar as regras, e duas passaram:
- uma classe CSS escrita diretamente em
globals.css; - um token de tema novo criado para a ocasião.
A primeira é trabalho de um linter de CSS. A segunda está dentro do sistema por definição, porque o token vive no seu tema. Os tokens e variantes novos ainda precisam de revisão humana: passar no lint não aprova o design.
Se você já fornece ao seu agente um sistema de design legível por máquina, como Astryx do Meta ou um arquivo DESIGN.md, @shadcn/lint é a metade que o torna obrigatório. Essas ferramentas dizem ao agente como é o sistema; esta verifica se ele o seguiu.
Está pronto para produção?
Pode ser usado hoje, mas é jovem. Em 28 de setembro de 2026, o pacote vai pela versão 0.2.0, publicada pela primeira vez em 14 de setembro. Teve sete publicações no npm em oito dias e nenhuma versão etiquetada no GitHub. Espere mudanças nos nomes de opções e na sintaxe das regras antes da 1.0, e fixe a versão em package.json. O que perdura é a ideia: um sistema de design expresso como verificações que o agente executa, não como diretrizes que se pede ao agente lembrar.