@ksmv/ui-checks 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0 - 2026-10-03
4
+
5
+ - **Contratos de tema 2.2.0 e de contraste 2.0.0** empacotados: dois derivados e dois pares do controle marcado (ver o CHANGELOG do `@ksmv/ui-react`). Um par novo pode reprovar um tema que hoje passa.
6
+ - **`tokens/contrast` resolve derivados não declarados** pela cadeia do contrato até um token declarado. Na 0.3.3 e anteriores, um par com um derivado que o tema não declarava ficava como cobertura pendente quando não havia evidência calculada.
7
+ - **A evidência pede a origem de um derivado usado num par de contraste.** Na 0.3.3 e anteriores, toda ponta de par era exigida com valor, e um derivado não declarado, que o navegador informa vazio, fazia a evidência ser recusada.
8
+ - **A identidade dos contratos muda**, então evidência capturada antes da atualização é recusada com código 2: quem usa `computedEvidence` precisa capturar de novo.
9
+
3
10
  ## 0.3.3 - 2026-10-02
4
11
 
5
12
  - **O erro de inicialização das regras diz a causa.** Na 0.3.2 e anteriores, evidência recusada ou contrato não resolvido saíam com código 2 e só com `Unable to initialize configured rules.`. O erro operacional agora diz qual foi o motivo e, quando há, o remédio: arquivo de evidência ausente, ilegível ou fora do formato; identidade do contrato diferente da instalada, com as duas versões; hash das fontes do tema diferente; seletores ou tema diferentes dos configurados; `capturedAt` inválido ou no futuro; tokens sem valor ou com `var(...)` restante, com os nomes dos tokens. A mensagem não repete valores do produto, e a saída continua sendo código 2.
package/README.md CHANGED
@@ -95,7 +95,7 @@ config Invalid computed theme evidence in artifacts/theme-evidence.json: the the
95
95
 
96
96
  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.`).
97
97
 
98
- 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 peso do botão por tamanho (`--ui-button-font-weight-sm`, `-md` e `-lg` derivam de `--ui-button-font-weight`), a cadeia é seguida até um token declarado.
98
+ 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 peso do botão por tamanho (`--ui-button-font-weight-sm`, `-md` e `-lg` derivam de `--ui-button-font-weight`), 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.
99
99
 
100
100
  Os tokens de densidade têm o valor conferido por `tokens/complete`: comprimento maior que zero em `px`, `rem` ou `em` para fonte e altura; também `0` para respiro e espaçamento; número sem unidade maior que zero para a entrelinha; em todos esses, também `calc()`, `clamp()`, `min()` ou `max()`; inteiro de 1 a 1000, `normal` ou `bold` para o peso. Com evidência, vale o valor capturado no navegador. Sem evidência, um valor que chega por `var()` é conferido pelo que ele alcança: o token declarado no mesmo tema ou, se não houver, o fallback do próprio `var()`; uma referência que não chega a valor nenhum fica como cobertura pendente. Valor inválido é cobertura obrigatória e não entra em baseline.
101
101
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "contractVersion": "1.0.0",
2
+ "contractVersion": "2.0.0",
3
3
  "schemaVersion": 1,
4
4
  "pairs": [
5
5
  { "foreground": "--ui-action-primary-text", "background": "--ui-action-primary-bg", "minimum": 4.5, "context": "normal-text" },
@@ -33,6 +33,8 @@
33
33
  { "foreground": "--ui-text-inverse", "background": "--ui-surface-inverse", "minimum": 4.5, "context": "inverse-text" },
34
34
  { "foreground": "--ui-border-control", "background": "--ui-surface", "minimum": 3, "context": "control-boundary" },
35
35
  { "foreground": "--ui-focus-ring", "background": "--ui-surface", "minimum": 3, "context": "focus-indicator" },
36
- { "foreground": "--ui-focus-ring", "background": "--ui-focus-ring-offset", "minimum": 3, "context": "focus-indicator" }
36
+ { "foreground": "--ui-focus-ring", "background": "--ui-focus-ring-offset", "minimum": 3, "context": "focus-indicator" },
37
+ { "foreground": "--ui-control-checked-bg", "background": "--ui-surface", "minimum": 3, "context": "checked-control-boundary" },
38
+ { "foreground": "--ui-control-checked-fg", "background": "--ui-control-checked-bg", "minimum": 3, "context": "checked-indicator" }
37
39
  ]
38
40
  }
package/dist/evidence.js CHANGED
@@ -54,16 +54,24 @@ function isIdentity(value) {
54
54
  hashPattern.test(value.sha256);
55
55
  }
56
56
  // Derived tokens are not required: a theme may leave them to their documented
57
- // default, the source token, and the browser then reports them as empty.
57
+ // default, the source token, and the browser then reports them as empty. A
58
+ // contrast endpoint that is such a token is required through its source.
58
59
  function requiredEvidenceTokens(contracts) {
59
60
  const derived = contracts.theme.derived;
61
+ const required = new Set(contracts.theme.required);
62
+ const origin = (token, seen = new Set()) => {
63
+ const source = derived[token];
64
+ if (required.has(token) || source === undefined || seen.has(token))
65
+ return token;
66
+ return origin(source, new Set([...seen, token]));
67
+ };
60
68
  return new Set([
61
- ...contracts.theme.required,
69
+ ...required,
62
70
  // A source that is itself derived is optional too; its chain is followed.
63
71
  ...Object.values(derived).filter((source) => !(source in derived)),
64
72
  ...contracts.contrast.pairs.flatMap(({ background, foreground }) => [
65
- background,
66
- foreground,
73
+ origin(background),
74
+ origin(foreground),
67
75
  ]),
68
76
  ]);
69
77
  }
@@ -12,7 +12,12 @@ export async function createDefaultRules({ components, computed, contract, manif
12
12
  createNoLiteralColorRule({ themes }),
13
13
  createTokenPrefixRule(),
14
14
  createTokenCompleteRule({ computed, contract: resolvedContract, themes }),
15
- createTokenContrastRule({ computed, manifest: resolvedManifest, themes }),
15
+ createTokenContrastRule({
16
+ computed,
17
+ derived: resolvedContract.derived,
18
+ manifest: resolvedManifest,
19
+ themes,
20
+ }),
16
21
  ...(components
17
22
  ? [createComponentOverrideRule({ classHelpers: components.classHelpers, sources: components.sources })]
18
23
  : []),
@@ -4,8 +4,9 @@ import type { CheckRule } from "../scan.js";
4
4
  export type { ContrastManifest, ContrastPair } from "../contracts.js";
5
5
  export interface TokenContrastOptions {
6
6
  computed?: Record<string, Record<string, string>>;
7
+ derived?: Record<string, string>;
7
8
  manifest: ContrastManifest;
8
9
  themes: ThemeConfig[];
9
10
  }
10
11
  export declare function loadContrastManifest(url?: URL): Promise<ContrastManifest>;
11
- export declare function createTokenContrastRule({ computed, manifest, themes, }: TokenContrastOptions): CheckRule;
12
+ export declare function createTokenContrastRule({ computed, derived, manifest, themes, }: TokenContrastOptions): CheckRule;
@@ -84,7 +84,7 @@ function dynamicStyleResult(file) {
84
84
  sourceFile.forEachChild(visit);
85
85
  return result;
86
86
  }
87
- export function createTokenContrastRule({ computed = {}, manifest, themes, }) {
87
+ export function createTokenContrastRule({ computed = {}, derived = {}, manifest, themes, }) {
88
88
  const collector = createThemeCollector(themes);
89
89
  return {
90
90
  name: "tokens/contrast",
@@ -130,6 +130,14 @@ export function createTokenContrastRule({ computed = {}, manifest, themes, }) {
130
130
  for (const [name, value] of Object.entries(computedTheme)) {
131
131
  tokens.set(name, value);
132
132
  }
133
+ // A derived token the theme leaves undeclared takes its documented
134
+ // default, the source token. resolveColor follows the chain and stops
135
+ // on a cycle.
136
+ for (const [token, source] of Object.entries(derived)) {
137
+ if (!tokens.has(token)) {
138
+ tokens.set(token, `var(${source})`);
139
+ }
140
+ }
133
141
  for (const pair of manifest.pairs) {
134
142
  const background = resolveColor(tokens.get(pair.background), tokens);
135
143
  const foreground = resolveColor(tokens.get(pair.foreground), tokens);
@@ -1,5 +1,5 @@
1
1
  {
2
- "contractVersion": "2.1.0",
2
+ "contractVersion": "2.2.0",
3
3
  "schemaVersion": 1,
4
4
  "prefix": "--ui-",
5
5
  "required": [
@@ -97,7 +97,9 @@
97
97
  "--ui-button-font-weight": "--ui-font-weight-strong",
98
98
  "--ui-button-font-weight-sm": "--ui-button-font-weight",
99
99
  "--ui-button-font-weight-md": "--ui-button-font-weight",
100
- "--ui-button-font-weight-lg": "--ui-button-font-weight"
100
+ "--ui-button-font-weight-lg": "--ui-button-font-weight",
101
+ "--ui-control-checked-bg": "--ui-action-primary-bg",
102
+ "--ui-control-checked-fg": "--ui-action-primary-text"
101
103
  },
102
104
  "brandComposition": [
103
105
  "--ui-brand-primary",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ksmv/ui-checks",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
4
4
  "description": "Static and computed quality checks for semantic UI token contracts.",
5
5
  "keywords": ["accessibility", "design-tokens", "quality", "static-analysis"],
6
6
  "license": "Apache-2.0",