npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ksmv/ui-checks

v0.21.1

Published

Static and computed quality checks for semantic UI token contracts.

Readme

@ksmv/ui-checks

Verificações de qualidade para o contrato semântico --ui-* de @ksmv/ui-react: completude dos temas, contraste dos pares do contrato (ações, estados, campos, controles e, desde o contrato de contraste 3.0.0, o texto base sobre as superfícies e, desde o 4.0.0, a cor de link e, desde o 5.0.0, a borda das ações e, desde o 6.0.0, as variantes de ação da 0.17.0 e o botão de texto e, desde o 7.0.0, os botões da casa da 0.18.1 e o hover por tom do botão de texto e, desde o 7.1.0, o texto de hover dos botões suaves e do contorno de aviso e, desde o 9.0.0, o texto, o limite de controle, o link, o botão de texto e o fantasma sobre a superfície inversa, a paleta categórica, a descrição do diálogo, o giro contra a superfície, o aviso e o rótulo de seção, o item de menu e o cartão de rádio, o cabeçalho da tabela e o grupo de alternância, e o selo cheio, a bolinha sobre a superfície inversa e a barra e, desde o 10.0.0, o fundo do botão de ícone contornado, a cor da ajuda e a caixa de seleção), prefixo dos tokens, cores literais fora dos arquivos de tema e evidência calculada vinculada às fontes.

Antes de depender deste pacote

Os pacotes são publicados no registro público do npm para que as aplicações do próprio mantenedor os instalem sem autenticação. O repositório de origem é privado. Não há equipe de suporte, canal público de issues nem compromisso de atendimento a terceiros. Este README é a documentação completa para quem está de fora.

Vulnerabilidades não devem ser publicadas em nenhum canal público. Quem não tem acesso ao repositório relata pelo suporte do próprio npm, que encaminha relatos sobre pacotes publicados: https://www.npmjs.com/support

Instalação e execução

npm install --save-dev @ksmv/ui-checks
npx ksmv-ui-checks ksmv-ui.config.js

Requer Node.js 22.14 ou superior. npx ksmv-ui-checks --help mostra os comandos; sem argumento, a configuração lida é ksmv-ui.config.js no diretório atual. --json troca o relatório legível por JSON.

Configuração

// ksmv-ui.config.js
import { defineConfig } from "@ksmv/ui-checks/config";

export default defineConfig({
  contractSource: "@ksmv/ui-react",
  root: ".",
  include: ["src/**/*.{css,tsx}"],
  themes: [
    { name: "light", files: ["src/theme.css"], selector: '[data-theme="light"]' },
    { name: "dark", files: ["src/theme.css"], selector: '[data-theme="dark"]' },
  ],
  baseline: ".ksmv-ui-baseline.json",
  failOnUncovered: true,
});

contractSource é obrigatório e explícito:

  • "@ksmv/ui-react": use quando a aplicação importa o pacote React. O verificador resolve os contratos da versão realmente instalada, então uma atualização do pacote é verificada contra os contratos novos;
  • "bundled": usa os contratos embutidos neste pacote, apenas para análises que não usam o pacote React, e falha fechado se encontrar essa importação.

O valor de selector deve ser textualmente idêntico ao seletor escrito no CSS, inclusive nas aspas. O verificador não tenta provar que grafias diferentes são equivalentes.

include precisa encontrar arquivos: nenhum arquivo analisado é falha, não sucesso. exclude e suppressions são opcionais.

Baseline

baseline é opcional. Quando configurado, o arquivo precisa existir. Crie-o somente depois de revisar a análise completa:

npx ksmv-ui-checks baseline ksmv-ui.config.js --write --first-seen-version=0.1.2 --owner-role=frontend --reduction-target=2099-12-31

O arquivo guarda fingerprints, regra, papel responsável e meta de redução, sem copiar o trecho encontrado. Uma atualização que aumente a dívida exige --allow-growth e uma justificativa em arquivo via --reason-file. Erro operacional e cobertura obrigatória ausente nunca entram na baseline.

O fingerprint segue o conteúdo do achado, como seletor, propriedade e valor, e a ordem entre achados idênticos no mesmo arquivo, não a linha. Linhas inseridas ou removidas em outro ponto do arquivo não transformam dívida conhecida em achado novo; mudar o próprio trecho, sim. Baselines gravadas pela 0.1.4 ou anterior usavam a posição e precisam ser regeneradas uma vez com a 0.1.5.

Códigos de saída

  • 0: análise completa, sem violações novas;
  • 1: existe violação nova ou regressão em relação à baseline;
  • 2: configuração inválida, leitura ou operação falhou, nenhum arquivo encontrado, ou há cobertura obrigatória não resolvida.

Trate 2 como falha do pipeline, não como violação comum.

Evidência calculada

Seletores disjuntos, como [data-theme="light"] e [data-theme="dark"], são verificados estaticamente e dispensam evidência. Quando a cascata de um tema legado se sobrepõe, como .theme e .theme.active, só o navegador sabe o valor final de cada token. Nesse caso o tema declara computedEvidence:

{
  name: "legacy",
  files: ["src/theme.css"],
  selector: ".theme.active",
  computedEvidence: { file: "artifacts/theme-evidence.json", theme: "legacy" },
}

A evidência não é uma exceção manual. O arquivo é recusado, com código 2, quando:

  • o arquivo não existe, não pode ser lido, não é JSON válido ou não tem a estrutura da receita abaixo;
  • capturedAt não é um horário UTC ou está no futuro;
  • os seletores diferem dos configurados, ou o tema nomeado em computedEvidence.theme não está no arquivo ou foi capturado para outro seletor;
  • o hash das fontes diverge, ou seja, o tema mudou depois da captura (o fim de linha não conta: o mesmo arquivo em CRLF e em LF tem o mesmo hash);
  • a identidade de um dos contratos diverge da instalada, ou seja, o pacote React mudou depois da captura;
  • falta algum token exigido, ou algum valor ainda contém var(...).

O erro operacional diz qual desses motivos ocorreu e, quando há, o remédio. Por exemplo, depois de alterar um arquivo de tema sem capturar de novo:

config Invalid computed theme evidence in artifacts/theme-evidence.json: the theme source hash differs, so a theme file changed after the capture. Capture the evidence again.

Depois de atualizar o pacote React, a mensagem traz a versão do contrato da captura e a do contrato instalado; quando faltam tokens ou resta var(...), traz os nomes dos tokens, em ordem, até dez, e a contagem dos demais. A mensagem não repete valores do produto: aparecem só o caminho da evidência e o nome do tema como estão na configuração, nomes de tokens e versões de contrato. Falha ao resolver os contratos também é relatada com a própria causa (Unable to resolve the configured contract source., Unsupported theme contract. ou Unsupported contrast manifest.).

Tokens opcionais, os derivados e os de marca, podem ficar vazios na captura: é assim que o navegador informa um token que o tema não declarou. Um derivado vazio assume o valor do token de origem, que é o padrão documentado no contrato. Quando a origem também é um derivado, como no texto do hover do contorno de aviso (--ui-action-warning-outline-text-hover deriva de --ui-action-warning-outline-text, que deriva do contorno de perigo), a cadeia é seguida até um token declarado. Um derivado que é ponta de um par de contraste, como --ui-control-checked-bg, também pode ficar vazio; a captura precisa então do token em que a cadeia dele termina. Sem evidência, tokens/contrast segue a mesma cadeia: um derivado que o tema não declara é avaliado pelo valor da origem, ou pelo padrão literal do contrato (transparent, uma cor). Um par com over mede um fundo translúcido composto sobre aquela superfície; sem over, um fundo translúcido continua cobertura pendente.

Desde o contrato de tema 3.0.0, a forma é da biblioteca: a regra themes/no-shape aponta, como violação, cada token da lista shape do contrato que um seletor de tema declarar. Quem migra de um tema que declarava forma pode carregar essas violações na baseline enquanto tira as linhas.

A captura usa a API pública deste pacote e um navegador à sua escolha. Com Playwright, e a aplicação servida localmente:

// capture-theme-evidence.mjs
import { mkdir, writeFile } from "node:fs/promises";
import { dirname, resolve } from "node:path";
import { computeThemeSourceHash, resolveContractSet } from "@ksmv/ui-checks";
import { chromium } from "playwright";

// Tudo aqui espelha ksmv-ui.config.js: o mesmo `root`, o mesmo
// `computedEvidence.file`, os mesmos arquivos de tema, e os mesmos nomes de
// `computedEvidence.theme` com seus seletores, cada um com uma URL em que o
// seletor está aplicado. O verificador resolve os caminhos a partir de `root`.
const root = resolve(".");
const evidenceFile = "artifacts/theme-evidence.json";
const themes = {
  legacy: { selector: ".theme.active", url: "http://localhost:4173/" },
};
const files = ["src/theme.css"];

const contracts = await resolveContractSet({ root, source: "@ksmv/ui-react" });
const tokens = [...new Set([
  ...contracts.theme.required,
  ...Object.keys(contracts.theme.derived),
  ...Object.values(contracts.theme.derived),
  ...contracts.theme.brandComposition,
  ...contracts.contrast.pairs.flatMap(({ background, foreground }) => [background, foreground]),
])];

const browser = await chromium.launch();
const captured = {};
for (const [name, { selector, url }] of Object.entries(themes)) {
  const page = await browser.newPage();
  await page.goto(url);
  const values = await page.evaluate(({ selector, tokens }) => {
    const style = getComputedStyle(document.querySelector(selector));
    return Object.fromEntries(tokens.map((token) => [token, style.getPropertyValue(token).trim()]));
  }, { selector, tokens });
  captured[name] = { selector, tokens: values };
  await page.close();
}
await browser.close();

const target = resolve(root, evidenceFile);
await mkdir(dirname(target), { recursive: true });
await writeFile(target, `${JSON.stringify({
  capturedAt: new Date().toISOString(),
  contracts: { contrast: contracts.identities.contrast, theme: contracts.identities.theme },
  schemaVersion: 1,
  selectors: [...new Set(Object.values(themes).map(({ selector }) => selector))],
  sourceHash: await computeThemeSourceHash(root, files),
  themes: captured,
}, null, 2)}\n`);

Capture de novo sempre que um arquivo de tema mudar ou o pacote React for atualizado; a verificação recusa a evidência antiga, que é exatamente o objetivo.

Ajustes em componentes

A regra components/override confere como o produto ajusta os componentes da biblioteca. Ela só roda quando a configuração tem o bloco components:

components: {
  include: ["src/**/*.tsx"],
  exclude: ["src/**/*.test.tsx"],
  sources: ["@ksmv/ui-react", "@/ui"],
  classHelpers: ["tw"],
}
  • Livre: o que encaixa o componente no pai — margem, largura, item de flex/grid (flex-1, self-*, col-span-*…), posição, z-*, visibilidade (hidden, sr-only) e alinhamento do texto. No style, as propriedades equivalentes.
  • Exige data-ui-override="motivo" no mesmo elemento: aparência (cor, fundo, borda, raio, sombra, fonte, altura, padding, gap-*, overflow-*), arranjo interno (flex, grid-cols-*, justify-*, items-*, block) e qualquer classe fora do catálogo, inclusive classes próprias do produto. Também exigem a marca className ou style que a regra não consegue ler e props repassadas depois de className ou style.
  • Relatório: sem marca, é uma violação (saída 1), que pode entrar em baseline durante a migração. Com marca, o elemento aparece em overrides, com o motivo.
  • Arquivos: os do components.include recebem só esta regra. Um arquivo que também está no include principal recebe as duas coisas, e aí a cobertura dinâmica volta a valer nele.
  • O que a regra não vê: um componente do produto que repassa props para um componente da biblioteca, e CSS do produto que mira as classes internas .ui-*; props repassadas sozinhas ou antes de className/style (<Button {...props} />); componente usado como valor (const X = Button; <X />); arquivos que não compilam — aparecem como erro operacional de parse (saída 2) e não são analisados pela regra.

Detalhes

  • sources substitui o padrão: mantenha @ksmv/ui-react na lista ao acrescentar um barril; a comparação é pelo especificador exato do import (subcaminhos e imports relativos precisam de entrada própria).
  • classHelpers soma aos padrões (cn, clsx, cx, classnames, classNames, twMerge, twJoin, cva) e entende chamadas no estilo clsx; de cva(...) só os textos literais são lidos. Template marcado (tw\...`) e funções de variante geradas por cva` contam como ilegíveis.
  • undefined, null, true, false e 0 dentro de uma expressão de classe não contribuem classe.
  • overrides[] tem path, line, column, component, reason e classes; classes lista as classes visuais autorizadas, as propriedades de style como style:<propriedade> e os tipos de achado (dynamic-class, dynamic-style, spread-after).
  • componente do produto com forwardRef que faz <Button className={cn("mt-2", className)} {...props} />: gera dynamic-class e spread-after; passe o spread antes do className e marque o elemento com o motivo ("repassa className do chamador"), ou use a baseline durante a migração.
  • o padrão responsivo hidden md:inline-flex exige marca (inline-flex é arranjo interno).
  • um components.include que não casa nenhum arquivo é erro operacional (saída 2).

API programática

O ponto de entrada @ksmv/ui-checks exporta runChecks, as regras, resolveContractSet, computeThemeSourceHash, loadComputedEvidence, as funções de baseline e os formatadores de relatório, com os tipos TypeScript correspondentes. Os contratos embutidos estão em @ksmv/ui-checks/theme-contract.json e @ksmv/ui-checks/contrast-contract.json.

Licença

Apache-2.0.