| Card não acompanha o tema escuro | .glass-strong (globals.css:795) fixa background: rgba(255,255,255,.82) e não tem nenhum override sob [data-tema="apol"]. Dentro do tema escuro o Card continua quase branco enquanto text-ink vira claro — texto claro em fundo claro. É a 2ª primitiva mais usada: 113 arquivos. | Declarar o fundo do .glass-strong pelo token --superficie (que já é 45 43 37 no escuro, globals.css:702) e adicionar o override do tema. O Button já foi corrigido assim — trocou bg-white por bg-superficie exatamente por esse motivo, e o comentário está no arquivo. |
| Input usa bg-white literal | Mesma armadilha, três derivados contaminados de uma vez: MaskedCurrencyInput, PasswordInput e UppercaseInput herdam o branco fixo. | Trocar por bg-superficie. O comentário do tailwind.config.ts já diz por que o token existe: “no lugar de bg-white literal — é o que permite um tema escuro sem reescrever componente”. |
| Três vocabulários pro mesmo semântico | Erro é danger no Badge e destructive no Button e no Progress — sendo que o Progress pinta bg-danger, o mesmo token com outro nome de prop. Azul é blue no Badge e primary no Button e no Progress. Três primitivas, três dicionários: quem escreve tem que lembrar de cor. | Escolher um dicionário e migrar. Sugestão: nome semântico (danger, primary, success) em todas, com alias temporário pro nome antigo até a última tela migrar. |
| Badge e Progress ainda não usam --success | O token existe (text-success, text-success-ink) desde 30/09 — antes disso as 26 mensagens com text-success saíam sem cor. Mas as primitivas continuam com verdes hardcoded (emerald-50/700 no Badge, emerald-500 no Progress) mais os do Avatar, SeletorDia, SeloAoVivo e DialogMeuPerfil. | Migrar Badge e Progress pro --success e derivar fundo do mesmo token com /15, como o Badge já faz em warning e danger. O success ficou de fora daquela correção e ainda usa o emerald-50 fixo que o próprio comentário condena. |
| Buracos na base: sem Textarea, Select, Checkbox, Radio | components/ui/ não tem nenhum dos quatro. O repo tem 55 <textarea>, 69 <select> e 13 checkbox escritos à mão, cada um repetindo — ou não — as classes do Input. Cada formulário novo é uma chance de divergir. | Criar as quatro primitivas com as classes do Input como base. Já existe referência de estilo pronta nos selects do FiltroTabela e do ComboSugestoes. |
| Input não tem estado de erro | Nenhuma prop error/invalid e nenhum estilo aria-invalid:. Os dois únicos aria-invalid do repo estão no form do SITE (form-orcamento.tsx:131,170), que nem usa esta primitiva — ou seja, o admin inteiro valida sem marcar o campo. | Estilo em aria-invalid:border-danger + aria-invalid:ring-danger/15 na própria primitiva, e o caller só passa aria-invalid + aria-describedby. Zero prop nova. |
| Button sem estado de loading | Nenhuma prop e nenhum spinner. Cada tela improvisa disabled={pending} + troca de rótulo (“Salvando…”, novo-cliente-dialog.tsx:251,255). É a interação mais repetida do admin e a única sem padrão na primitiva. | Prop loading?: boolean que aplica disabled, aria-busy e um spinner no lugar do ícone, preservando a largura do botão (senão a tela pula ao clicar). |
| Button size="icon" é variante morta | Declarada no cva (h-11 w-11) e com zero usos em app/ e components/. Botão só de ícone hoje é feito com size="sm" + padding manual — o oposto de um sistema. | Ou se adota (e os botões de ícone do admin migram, ganhando alvo de toque de 44px), ou se remove do cva. Variante que ninguém usa é código que mente sobre o padrão. |
| focus: vs focus-visible: | Input usa focus:ring-2 ring-blue/15 (dispara também no clique de mouse); Button e Switch usam focus-visible:ring-2 ring-blue/40. Gatilho diferente, opacidade diferente — e o ring-offset do Switch vem sem cor, então cai no branco default do Tailwind e some sobre fundo escuro. | Um anel só pro sistema: focus-visible:ring-2 ring-blue/40 ring-offset-2 ring-offset-bg nas três. Campo de texto pode manter a borda azul no focus:, que é dica de edição, mas o anel é do teclado. |
| Dois mecanismos de press, e cores cruas fora do token | Button embute active:scale-[0.96] no cva; Switch e o olho do PasswordInput usam a classe .press — mesma transformação, dois caminhos, e o comentário do Button ainda sugere que são a mesma coisa. Na mesma linha: Switch off em bg-[#cbd5e1], Button em text-[#fff], e text-[#fff] convivendo com text-white em oito arquivos dos padrões compostos. | Deixar o .press como único mecanismo (é o que os padrões compostos usam) e trocar os hex crus pelos tokens — --line no trilho off, uma escrita só pro branco. |
| Cinco exports não encaminham ref nem espalham props | Badge, Progress, Skeleton e Switch são funções simples. Consequência concreta e medível: Skeleton não aceita nem aria-hidden; Progress não aceita aria-label, então a barra de painel-whats.tsx:985 é um role="progressbar" sem nome acessível; Switch tipa aria-label mas não aria-labelledby. | forwardRef + spread de ...rest nas quatro. Mudança pequena, destrava acessibilidade e composição. |